| using System; | 
| using System.Collections.Generic; | 
| using System.Linq; | 
| using System.Linq.Expressions; | 
| using System.Reflection; | 
| using System.Reflection.Emit; | 
| using System.Text; | 
| using Jace.Operations; | 
| using Jace.Util; | 
|   | 
| namespace Jace.Execution | 
| { | 
|     public class DynamicCompiler : IExecutor | 
|     { | 
|         private string FuncAssemblyQualifiedName; | 
|         private readonly bool caseSensitive; | 
|   | 
|         public DynamicCompiler(): this(false) { } | 
|         public DynamicCompiler(bool caseSensitive) | 
|         { | 
|             this.caseSensitive = caseSensitive; | 
|             // The lower func reside in mscorelib, the higher ones in another assembly. | 
|             // This is  an easy cross platform way to to have this AssemblyQualifiedName. | 
|             FuncAssemblyQualifiedName = | 
|                 typeof(Func<double, double, double, double, double, double, double, double, double, double>).GetTypeInfo().Assembly.FullName; | 
|         } | 
|   | 
|         public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry) | 
|         { | 
|             return Execute(operation, functionRegistry, constantRegistry, new Dictionary<string, double>()); | 
|         } | 
|   | 
|         public double Execute(Operation operation, IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry,  | 
|             IDictionary<string, double> variables) | 
|         { | 
|             return BuildFormula(operation, functionRegistry, constantRegistry)(variables); | 
|         } | 
|   | 
|         public Func<IDictionary<string, double>, double> BuildFormula(Operation operation, | 
|             IFunctionRegistry functionRegistry, IConstantRegistry constantRegistry) | 
|         { | 
|             Func<FormulaContext, double> func = BuildFormulaInternal(operation, functionRegistry); | 
|             return caseSensitive | 
|                 ? (Func<IDictionary<string, double>, double>)(variables => | 
|                 { | 
|                     return func(new FormulaContext(variables, functionRegistry, constantRegistry)); | 
|                 }) | 
|                 : (Func<IDictionary<string, double>, double>)(variables => | 
|                 { | 
|                     variables = EngineUtil.ConvertVariableNamesToLowerCase(variables); | 
|                     FormulaContext context = new FormulaContext(variables, functionRegistry, constantRegistry); | 
|                     return func(context); | 
|                 }); | 
|         } | 
|   | 
|         private Func<FormulaContext, double> BuildFormulaInternal(Operation operation,  | 
|             IFunctionRegistry functionRegistry) | 
|         { | 
|             ParameterExpression contextParameter = Expression.Parameter(typeof(FormulaContext), "context"); | 
|   | 
|             LabelTarget returnLabel = Expression.Label(typeof(double)); | 
|   | 
|             Expression<Func<FormulaContext, double>> lambda = Expression.Lambda<Func<FormulaContext, double>>( | 
|                 GenerateMethodBody(operation, contextParameter, functionRegistry), | 
|                 contextParameter | 
|             ); | 
|             return lambda.Compile(); | 
|         } | 
|   | 
|          | 
|   | 
|         private Expression GenerateMethodBody(Operation operation, ParameterExpression contextParameter, | 
|             IFunctionRegistry functionRegistry) | 
|         { | 
|             if (operation == null) | 
|                 throw new ArgumentNullException("operation"); | 
|   | 
|             if (operation.GetType() == typeof(IntegerConstant)) | 
|             { | 
|                 IntegerConstant constant = (IntegerConstant)operation; | 
|   | 
|                 double value = constant.Value; | 
|                 return Expression.Constant(value, typeof(double)); | 
|             } | 
|             else if (operation.GetType() == typeof(FloatingPointConstant)) | 
|             { | 
|                 FloatingPointConstant constant = (FloatingPointConstant)operation; | 
|   | 
|                 return Expression.Constant(constant.Value, typeof(double)); | 
|             } | 
|             else if (operation.GetType() == typeof(Variable)) | 
|             { | 
|                 Variable variable = (Variable)operation; | 
|   | 
|                 Func<string, FormulaContext, double> getVariableValueOrThrow = PrecompiledMethods.GetVariableValueOrThrow; | 
|                 return Expression.Call(null, | 
|                     getVariableValueOrThrow.GetMethodInfo(), | 
|                     Expression.Constant(variable.Name), | 
|                     contextParameter); | 
|             } | 
|             else if (operation.GetType() == typeof(Multiplication)) | 
|             { | 
|                 Multiplication multiplication = (Multiplication)operation; | 
|                 Expression argument1 = GenerateMethodBody(multiplication.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(multiplication.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Multiply(argument1, argument2); | 
|             } | 
|             else if (operation.GetType() == typeof(Addition)) | 
|             { | 
|                 Addition addition = (Addition)operation; | 
|                 Expression argument1 = GenerateMethodBody(addition.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(addition.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Add(argument1, argument2); | 
|             } | 
|             else if (operation.GetType() == typeof(Subtraction)) | 
|             { | 
|                 Subtraction addition = (Subtraction)operation; | 
|                 Expression argument1 = GenerateMethodBody(addition.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(addition.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Subtract(argument1, argument2); | 
|             } | 
|             else if (operation.GetType() == typeof(Division)) | 
|             { | 
|                 Division division = (Division)operation; | 
|                 Expression dividend = GenerateMethodBody(division.Dividend, contextParameter, functionRegistry); | 
|                 Expression divisor = GenerateMethodBody(division.Divisor, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Divide(dividend, divisor); | 
|             } | 
|             else if (operation.GetType() == typeof(Modulo)) | 
|             { | 
|                 Modulo modulo = (Modulo)operation; | 
|                 Expression dividend = GenerateMethodBody(modulo.Dividend, contextParameter, functionRegistry); | 
|                 Expression divisor = GenerateMethodBody(modulo.Divisor, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Modulo(dividend, divisor); | 
|             } | 
|             else if (operation.GetType() == typeof(Exponentiation)) | 
|             { | 
|                 Exponentiation exponentation = (Exponentiation)operation; | 
|                 Expression @base = GenerateMethodBody(exponentation.Base, contextParameter, functionRegistry); | 
|                 Expression exponent = GenerateMethodBody(exponentation.Exponent, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Call(null, typeof(Math).GetRuntimeMethod("Pow", new Type[] { typeof(double), typeof(double) }), @base, exponent); | 
|             } | 
|             else if (operation.GetType() == typeof(UnaryMinus)) | 
|             { | 
|                 UnaryMinus unaryMinus = (UnaryMinus)operation; | 
|                 Expression argument = GenerateMethodBody(unaryMinus.Argument, contextParameter, functionRegistry); | 
|                 return Expression.Negate(argument); | 
|             } | 
|             else if (operation.GetType() == typeof(And)) | 
|             { | 
|                 And and = (And)operation; | 
|                 Expression argument1 = Expression.NotEqual(GenerateMethodBody(and.Argument1, contextParameter, functionRegistry), Expression.Constant(0.0)); | 
|                 Expression argument2 = Expression.NotEqual(GenerateMethodBody(and.Argument2, contextParameter, functionRegistry), Expression.Constant(0.0)); | 
|   | 
|                 return Expression.Condition(Expression.And(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(Or)) | 
|             { | 
|                 Or and = (Or)operation; | 
|                 Expression argument1 = Expression.NotEqual(GenerateMethodBody(and.Argument1, contextParameter, functionRegistry), Expression.Constant(0.0)); | 
|                 Expression argument2 = Expression.NotEqual(GenerateMethodBody(and.Argument2, contextParameter, functionRegistry), Expression.Constant(0.0)); | 
|   | 
|                 return Expression.Condition(Expression.Or(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(LessThan)) | 
|             { | 
|                 LessThan lessThan = (LessThan)operation; | 
|                 Expression argument1 = GenerateMethodBody(lessThan.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(lessThan.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Condition(Expression.LessThan(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(LessOrEqualThan)) | 
|             { | 
|                 LessOrEqualThan lessOrEqualThan = (LessOrEqualThan)operation; | 
|                 Expression argument1 = GenerateMethodBody(lessOrEqualThan.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(lessOrEqualThan.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Condition(Expression.LessThanOrEqual(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(GreaterThan)) | 
|             { | 
|                 GreaterThan greaterThan = (GreaterThan)operation; | 
|                 Expression argument1 = GenerateMethodBody(greaterThan.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(greaterThan.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Condition(Expression.GreaterThan(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(GreaterOrEqualThan)) | 
|             { | 
|                 GreaterOrEqualThan greaterOrEqualThan = (GreaterOrEqualThan)operation; | 
|                 Expression argument1 = GenerateMethodBody(greaterOrEqualThan.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(greaterOrEqualThan.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Condition(Expression.GreaterThanOrEqual(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(Equal)) | 
|             { | 
|                 Equal equal = (Equal)operation; | 
|                 Expression argument1 = GenerateMethodBody(equal.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(equal.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Condition(Expression.Equal(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(NotEqual)) | 
|             { | 
|                 NotEqual notEqual = (NotEqual)operation; | 
|                 Expression argument1 = GenerateMethodBody(notEqual.Argument1, contextParameter, functionRegistry); | 
|                 Expression argument2 = GenerateMethodBody(notEqual.Argument2, contextParameter, functionRegistry); | 
|   | 
|                 return Expression.Condition(Expression.NotEqual(argument1, argument2), | 
|                     Expression.Constant(1.0), | 
|                     Expression.Constant(0.0)); | 
|             } | 
|             else if (operation.GetType() == typeof(Function)) | 
|             { | 
|                 Function function = (Function)operation; | 
|   | 
|                 FunctionInfo functionInfo = functionRegistry.GetFunctionInfo(function.FunctionName); | 
|                 Type funcType; | 
|                 Type[] parameterTypes; | 
|                 Expression[] arguments; | 
|   | 
|                 if (functionInfo.IsDynamicFunc) | 
|                 { | 
|                     funcType = typeof(DynamicFunc<double, double>); | 
|                     parameterTypes = new Type[] { typeof(double[]) }; | 
|   | 
|   | 
|                     Expression[] arrayArguments = new Expression[function.Arguments.Count]; | 
|                     for (int i = 0; i < function.Arguments.Count; i++) | 
|                         arrayArguments[i] = GenerateMethodBody(function.Arguments[i], contextParameter, functionRegistry); | 
|   | 
|                     arguments = new Expression[1]; | 
|                     arguments[0] = NewArrayExpression.NewArrayInit(typeof(double), arrayArguments); | 
|                 } | 
|                 else | 
|                 { | 
|                     funcType = GetFuncType(functionInfo.NumberOfParameters); | 
|                     parameterTypes = (from i in Enumerable.Range(0, functionInfo.NumberOfParameters) | 
|                                              select typeof(double)).ToArray(); | 
|   | 
|                     arguments = new Expression[functionInfo.NumberOfParameters]; | 
|                     for (int i = 0; i < functionInfo.NumberOfParameters; i++) | 
|                         arguments[i] = GenerateMethodBody(function.Arguments[i], contextParameter, functionRegistry); | 
|                 } | 
|   | 
|                 Expression getFunctionRegistry = Expression.Property(contextParameter, "FunctionRegistry"); | 
|   | 
|                 ParameterExpression functionInfoVariable = Expression.Variable(typeof(FunctionInfo)); | 
|   | 
|                 Expression funcInstance; | 
|                 if (!functionInfo.IsOverWritable) | 
|                 { | 
|                     funcInstance = Expression.Convert( | 
|                         Expression.Property( | 
|                             Expression.Call( | 
|                                 getFunctionRegistry, | 
|                                 typeof(IFunctionRegistry).GetRuntimeMethod("GetFunctionInfo", new Type[] { typeof(string) }), | 
|                                 Expression.Constant(function.FunctionName)), | 
|                             "Function"), | 
|                         funcType); | 
|                 } | 
|                 else | 
|                     funcInstance = Expression.Constant(functionInfo.Function, funcType); | 
|   | 
|                 return Expression.Call( | 
|                     funcInstance, | 
|                     funcType.GetRuntimeMethod("Invoke", parameterTypes), | 
|                     arguments); | 
|             } | 
|             else | 
|             { | 
|                 throw new ArgumentException(string.Format("Unsupported operation \"{0}\".", operation.GetType().FullName), "operation"); | 
|             } | 
|         } | 
|   | 
|         private Type GetFuncType(int numberOfParameters) | 
|         { | 
|             string funcTypeName; | 
|             if (numberOfParameters < 9) | 
|                 funcTypeName = string.Format("System.Func`{0}", numberOfParameters + 1); | 
|             else | 
|                 funcTypeName = string.Format("System.Func`{0}, {1}", numberOfParameters + 1, FuncAssemblyQualifiedName); | 
|             Type funcType = Type.GetType(funcTypeName); | 
|   | 
|             Type[] typeArguments = new Type[numberOfParameters + 1]; | 
|             for (int i = 0; i < typeArguments.Length; i++) | 
|                 typeArguments[i] = typeof(double); | 
|   | 
|             return funcType.MakeGenericType(typeArguments); | 
|         } | 
|   | 
|         private static class PrecompiledMethods | 
|         { | 
|             public static double GetVariableValueOrThrow(string variableName, FormulaContext context) | 
|             { | 
|                 if (context.Variables.TryGetValue(variableName, out double result)) | 
|                     return result; | 
|                 else if (context.ConstantRegistry.IsConstantName(variableName)) | 
|                     return context.ConstantRegistry.GetConstantInfo(variableName).Value; | 
|                 else | 
|                     throw new VariableNotDefinedException($"The variable \"{variableName}\" used is not defined."); | 
|             } | 
|         } | 
|     } | 
| } |