public Expression GetExecuteFunctionExpression(string funcname, Expression argexp, Expression inputContextParam) { Expression argexparray = ExCasts.WrapArguments(argexp); return(Expression.Call(Expression.Constant(this), this.GetType().GetRuntimeMethod("ExecuteFunction", new[] { typeof(string), typeof(object[]), typeof(InputContext) }), Expression.Constant(funcname), argexparray, inputContextParam)); }
public static Expression GetArgAsBool(Expression argexp) { Expression bexp = ExCasts.UnwrapVariable(argexp); // see note in GetArgAsDouble //if (bexp.Type == typeof(object[])) // bexp = Expression.ArrayIndex(bexp, Expression.Constant(0)); return(ExCasts.GetCastExpression(Expression.Constant("c:b"), bexp)); }
public static Expression GetArgAsString(Expression argexp) { Expression sexp = ExCasts.UnwrapVariable(argexp); // see note in GetArgAsDouble //if (sexp.Type == typeof(object[])) // sexp = Expression.ArrayIndex(sexp, Expression.Constant(0)); return(Expression.Convert(sexp, typeof(string))); }
public static Expression GetArgAsObject(Expression argexp) { // TODO: can we check for object[] here safely? it doesn't seem possible // may need an alternative to these special case object[] checks in the // getarg methods. The issue arrises when a library is passed in array args // or a:all and is expecting a single arg // maybe we could resolve this by always array-casting the args even if // there's only one, and then expecting the library to index ? return(Expression.Convert(ExCasts.UnwrapVariable(argexp), typeof(object))); }
public Expression GetFunctionExpression(string funcname, Expression argexp, Expression argParams, ParameterExpression inputParams, Expression inputContextParam, List <InputVar> compiledInputVarsList) { // we can't precompile these, unfortunately. // so instead, use a dynamic call Expression argexparray = ExCasts.WrapArguments(argexp); return(Expression.Call(Expression.Constant(this), this.GetType().GetRuntimeMethod("ExecuteFunction", new[] { typeof(string), typeof(object[]), typeof(InputContext) }), Expression.Constant(funcname), argexparray, inputContextParam)); }
public static Expression GetArgIndexUnbox(Expression argexp, int index) { if (!(argexp is NewArrayExpression)) { // need to do things the worse way, because we can't do this optimization return(Expression.ArrayAccess(ExCasts.UnwrapVariable(argexp), Expression.Constant(index))); } NewArrayExpression arx = argexp as NewArrayExpression; return(arx.Expressions[index]); }
public static Expression GetArgAsDouble(Expression argexp) { // see below comment on GetArgAsInt Expression dexp = ExCasts.UnwrapVariable(argexp); // special case: in specifically the scenario where we are reading args for a library // method, if we expect only one arg and ask e.g. GetArgAsDouble(args), in some cases // the args may be represented as object[] { arg0 } rather than just arg0 //if (dexp.Type == typeof(object[])) // dexp = Expression.ArrayIndex(dexp, Expression.Constant(0)); return(ExCasts.GetExpressionObjectAsDouble(dexp)); //return Expression.Convert(ExCasts.UnwrapVariable(argexp), typeof(double)); }
public static Expression GetExpressionObjectAsDouble(Expression exp) { if (exp.Type == typeof(double)) { return(exp); } if (exp.Type == typeof(int)) { return(Expression.Convert(ExCasts.UnwrapVariable(exp), typeof(double))); } return(Expression.Call(typeof(ExCasts).GetTypeInfo().GetDeclaredMethod("GetObjectAsDouble"), exp)); }
public static Expression GetArgAsInt(Expression argexp) { // so: going through convert directly means that auto-casting like double -> int // is not done for us. If we use this instead, we can have that luxury // additionally, I want to move the logic for this out of exfuncs and into excasts // as that locale is more appropriate Expression iexp = ExCasts.UnwrapVariable(argexp); // see note in GetArgAsDouble //if (iexp.Type == typeof(object[])) // iexp = Expression.ArrayIndex(iexp, Expression.Constant(0)); return(ExCasts.GetExpressionObjectAsInt(iexp)); //return Expression.Convert(ExCasts.UnwrapVariable(argexp), typeof(int)); }
private static object ArrayRecurseDefault(object[] sizes, object def, int sizeindex) { if (sizes.Length <= sizeindex) { return(def); } int size = ExCasts.GetObjectAsInt(ExCasts.UnwrapAtRuntime(sizes[sizeindex])); sizeindex++; object[] arr = new object[size]; for (int i = 0; i < size; i++) { arr[i] = ArrayRecurseDefault(sizes, def, sizeindex); } return(arr); }
public static object[] Array(object[] args) { if (args[0] is double) { args[0] = (int)((double)args[0]); } if (args[0] is int) { int size = (int)args[0]; object[] arr = new object[size]; if (args.Length > 1) { for (int i = 0; i < size; i++) { arr[i] = args[1]; } } return(arr); } else if (args[0] is object[]) { object[] sizes = (object[])args[0]; int size = ExCasts.GetObjectAsInt(ExCasts.UnwrapAtRuntime(sizes[0])); object[] arr = new object[size]; if (args.Length <= 1) { for (int i = 0; i < size; i++) { arr[i] = ArrayRecurse(sizes, 1); } } else { for (int i = 0; i < size; i++) { arr[i] = ArrayRecurseDefault(sizes, args[1], 1); } } return(arr); } throw new Exception("Array creation failed because type of sizes was not recognized"); }
public static Expression GetArgsAsDouble(Expression args) { return(Expression.Call( typeof(ExCasts).GetTypeInfo().GetDeclaredMethod("GetObjectsAsDoubleUnwrapping"), ExCasts.WrapArguments(args))); }
public static Expression GetFunctionExpression(Expression funcexp, Expression argexp, List <InputContext> compiledContexts, int scopeContext, Expression argParams, ParameterExpression inputParams, ParameterExpression contextParams, List <InputVar> compiledInputVarsList) { // special case: library function bool doDynamicLibraryCall = false; if (funcexp is ConstantExpression && funcexp.Type == typeof(LibraryFunctionIntermediate)) { LibraryFunctionIntermediate lfi = (funcexp as ConstantExpression).Value as LibraryFunctionIntermediate; if (lfi.Library == null) { // library hasn't been populated yet, so we have to do dynamic call doDynamicLibraryCall = true; } else { if (!(argexp is NewArrayExpression)) { argexp = ExCasts.WrapArguments(argexp); } return(lfi.Library.GetFunctionExpression(lfi.FuncName, argexp, argParams, inputParams, Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)), compiledInputVarsList)); } } else if (funcexp.Type == typeof(LibraryFunctionIntermediate)) { doDynamicLibraryCall = true; } if (doDynamicLibraryCall) { // the dynamic case: the library function intermediate can't be rendered at compiled time // TODO: put some explanation here as to how and why this happens throw new Exception("Attempted to access library dynamically, which is not supported."); /*Expression argexparray = ExCasts.WrapArguments(argexp); * return Expression.Call( * Expression.Field(funcexp, typeof(LibraryFunctionIntermediate), "LibraryPointer"), * typeof(LibraryPointer).GetRuntimeMethod( * "ExecuteFunction", * new[] { typeof(string), typeof(object[]), typeof(InputContext) }), * Expression.Field(funcexp, typeof(LibraryFunctionIntermediate), "FuncName"), * argexparray, * Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)));*/ } // special case: object was accessed if (funcexp.Type == typeof(AccessorResult)) { // this can't be rendered at compile time so we need to call out at runtime // to try to access the func that may be in the access result Expression argexparray = ExCasts.WrapArguments(argexp); return(Expression.Call(funcexp, typeof(AccessorResult).GetRuntimeMethod("ExecuteFunc", new Type[] { typeof(object[]) }), argexparray)); } string funcname = (funcexp as ConstantExpression).Value as string; switch (funcname.ToLower()) { //case "f:index": // return Expression.ArrayIndex(argexp, // Expression.Add(Expression.Constant(1), Expression.Convert(Expression.ArrayIndex(argexp, Expression.Constant(0)), typeof(int)))); // //Expression.Convert(Expression.ArrayIndex(argexp, Expression.Subtract(Expression.ArrayLength(argexp), Expression.Constant(1))), typeof(int))); case "f:print": return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("Print"), GetArgAsString(argexp), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); case "f:return": // todo: make a returnlabel at the end of the expression and return to it //return Expression.Return() // this needs to be done with a special operator instead throw new Exception("TODO"); case "f:import": // load a library // switching this so it happens at compiletime // doing some really convoluted unwrapping here... ExLibs.ImportLibrary( (string)(((argexp as NewArrayExpression).Expressions[0] as UnaryExpression).Operand as ConstantExpression).Value, (string)(((argexp as NewArrayExpression).Expressions[1] as UnaryExpression).Operand as ConstantExpression).Value, compiledContexts[scopeContext]); return(Expression.Constant(0)); /*return Expression.Call(typeof(ExLibs).GetTypeInfo().GetDeclaredMethod("ImportLibrary"), * GetArgAsString(GetArgIndex(argexp, 0)), * GetArgAsString(GetArgIndex(argexp, 1)), * Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)));*/ case "f:while": ParameterExpression whileresult = Expression.Parameter(typeof(object), "whileresult"); LabelTarget breaklabel = Expression.Label("WhileBreak"); return(Expression.Block( new[] { whileresult }, Expression.Assign(whileresult, Expression.Convert(Expression.Constant(0), typeof(object))), Expression.Loop( Expression.IfThenElse( GetArgAsBool(GetArgIndexUnbox(argexp, 0)), Expression.Assign(whileresult, Expression.Convert(GetArgIndexUnbox(argexp, 1), typeof(object))), Expression.Break(breaklabel, whileresult) ), breaklabel ), whileresult )); // TODO: evaluate performance loss of the above version vs this... // old version that returns 0 always: /*LabelTarget breaklabel = Expression.Label("WhileBreak"); * return Expression.Block( * Expression.Loop( * Expression.IfThenElse( * GetArgAsBool(GetArgIndexUnbox(argexp, 0)), * GetArgIndexUnbox(argexp, 1), * Expression.Break(breaklabel) * ), * breaklabel * ), * Expression.Constant(0) * );*/ case "f:for": throw new Exception("TODO"); return(null); /*ParameterExpression foriterator = Expression.Parameter(typeof(int), "foriterator"); * LabelTarget breakforlabel = Expression.Label("ForBreak"); * return Expression.Block( * Expression.Loop( * Expression.IfThenElse( * GetArgIndex(argexp, 0), * GetArgIndex(argexp, 1), * Expression.Break(breakforlabel) * ), * breakforlabel * ), * Expression.Constant(0) * );*/ case "f:if": ParameterExpression ifresult = Expression.Parameter(typeof(object), "ifresult"); // if three args were given, divert to ifelse if (argexp is NewArrayExpression && (argexp as NewArrayExpression).Expressions.Count > 2) { return(Expression.Block( new[] { ifresult }, Expression.IfThenElse( GetArgAsBool(GetArgIndex(argexp, 0)), Expression.Assign(ifresult, GetArgAsObject(GetArgIndex(argexp, 1))), Expression.Assign(ifresult, GetArgAsObject(GetArgIndex(argexp, 2)))), ifresult)); } return(Expression.Block( new[] { ifresult }, Expression.IfThenElse( GetArgAsBool(GetArgIndex(argexp, 0)), Expression.Assign(ifresult, GetArgAsObject(GetArgIndex(argexp, 1))), Expression.Assign(ifresult, GetArgAsObject(Expression.Constant(0.0)))), ifresult)); case "f:ifelse": ParameterExpression ifelseresult = Expression.Parameter(typeof(object), "ifelseresult"); return(Expression.Block( new[] { ifelseresult }, Expression.IfThenElse( GetArgAsBool(GetArgIndex(argexp, 0)), Expression.Assign(ifelseresult, GetArgAsObject(GetArgIndex(argexp, 1))), Expression.Assign(ifelseresult, GetArgAsObject(GetArgIndex(argexp, 2)))), ifelseresult)); case "f:newfunc": // branching behavior here: // if arg[1] is a string, we'll compile by text // if arg[1] is not a string, we'll treat it as the expression to use for the new func Expression newfuncarg1 = GetArgIndex(argexp, 1); // THOUGHT: I don't want to unwrap the var that returns from a func, right? // it would be better to return it as a var. Why were we unwrapping it here? // maybe we just thought that a func should return a primitive? that's not right //newfuncarg1 = ExCasts.UnwrapVariable(newfuncarg1); if (newfuncarg1.Type != typeof(string)) // NOTE: disabling this case... this case would handle dynamic compilation where we // would allow a string to be constructed at runtime and then compiled, like // "f:newfunc(f:test, "a:0 +" + "51")" // but this makes compilation fail for string result functions // later we can reintroduce this with a special function name like f:newdynamicfunc // that will expect this behavior //&& !(newfuncarg1 is UnaryExpression && (newfuncarg1 as UnaryExpression).Operand.Type == typeof(string))) { // TODO: remove this comments... // left off here // debating just doing a preproc to transform this arg into a string // for this specific function so that we don't need to handle this case // some downsides though: // 1. may violate expectations? // 2. will be much less efficient than if I can get this working // but getting this working means // 1. line needs to store its paramexpressions so they can be ref'd by other lines // 2. need a way to pass this line thru to the method // 3. (hardest) need a way to pass the Expression itself as an arg to the method... // could I: // 1. take the expression block HERE, outside of the exp. tree // 2. compile it witohut using expression.call, so new funcs are compiled // at compile time // 3. call a method and pass the resulting exline.Compiled lambda into it // 4. that method just replaces the func's compiled with ^ this one // would that actually violate anything? CompileContext funcCompile = new CompileContext(newfuncarg1, (argParams as UnaryExpression).Operand as ParameterExpression, // this is because argparams // gets wrapped in a convert (object[]) , so we need to unwrap it here... inputParams, contextParams, compiledInputVarsList, compiledContexts.ToArray(), scopeContext); return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("NewFuncCompiled"), GetArgAsString(GetArgIndex(argexp, 0)), Expression.Constant(funcCompile), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); } return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("NewFunc"), GetArgAsString(GetArgIndex(argexp, 0)), GetArgAsString(GetArgIndex(argexp, 1)), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); case "f:setfunc": Expression setfuncarg1 = GetArgIndex(argexp, 1); setfuncarg1 = ExCasts.UnwrapVariable(setfuncarg1); if (setfuncarg1.Type != typeof(string)) // see above note for f:newfunc //&& !(setfuncarg1 is UnaryExpression && (setfuncarg1 as UnaryExpression).Operand.Type == typeof(string))) { CompileContext funcCompile = new CompileContext(setfuncarg1, (argParams as UnaryExpression).Operand as ParameterExpression, // this is because argparams // gets wrapped in a convert (object[]) , so we need to unwrap it here... inputParams, contextParams, compiledInputVarsList, compiledContexts.ToArray(), scopeContext); return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("SetFuncCompiled"), GetArgAsString(GetArgIndex(argexp, 0)), Expression.Constant(funcCompile), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); } return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("SetFunc"), GetArgAsString(GetArgIndex(argexp, 0)), GetArgAsString(GetArgIndex(argexp, 1)), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); case "f:newvar": // change: if possible, define at compiletime so we can store vars at compiletime { // remove this... /*Expression arg0 = (argexp as NewArrayExpression).Expressions[0]; * if (arg0 is UnaryExpression) * { * // can only optimize here if the varname is constant * Expression arg0op = (arg0 as UnaryExpression).Operand; * if(arg0op is BinaryExpression) * { * InputVar argvar = compiledInputVarsList[(int)((arg0op as BinaryExpression).Right as ConstantExpression).Value]; * compiledContexts[scopeContext].InsertVar(argvar); * } * }*/ return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("NewVar"), GetArgAsInputVarName(GetArgIndex(argexp, 0)), GetArgAsObject(GetArgIndex(argexp, 1)), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); } case "f:setvar": return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("SetVar"), GetArgAsInputVar(GetArgIndex(argexp, 0), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext))), GetArgAsObject(GetArgIndex(argexp, 1)))); case "f:getvar": return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("LookupVar"), GetArgAsString(argexp), Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)))); case "f:array": return(Expression.Call(typeof(ExFuncs).GetTypeInfo().GetDeclaredMethod("Array"), ExCasts.WrapArguments(argexp))); default: // we didn't recognize the function, we should check if it's part of the // custom defined functions dictionary Expression argexparray = ExCasts.WrapArguments(argexp); return(Expression.Call(GetFuncExpression( Expression.ArrayIndex(contextParams, Expression.Constant(scopeContext)), funcexp), typeof(Exline).GetRuntimeMethod("Execute", new[] { typeof(object[]) }), argexparray)); // no longer doing precompile function routing due to elimination of placeholders for scoping // may achieve performance gains in the future by reinstation placeholders to allow compiler // to find pointer to function ahead of runtime // TODO /*Exline exl = context.GetFunc(funcname.ToLower()); * if (exl != null) * { * //Expression argexparray = Expression.NewArrayInit(typeof(object), Expression.Convert(argexp, typeof(object))); * //Expression argexparray = argexp; * //if (!(argexp is NewArrayExpression)) * // argexparray = Expression.NewArrayInit(typeof(object), Expression.Convert(argexp, typeof(object))); * //else * //argexparray = Expression.NewArrayInit(typeof(object), argexp); * //argexparray = Expression.Convert(argexp, typeof(object[])); * //Expression argexparray = Expression.Convert(argexp, typeof(object)); * Expression argexparray = ExCasts.WrapArguments(argexp); * return Expression.Call(GetFuncExpression(Expression.Constant(context), funcexp), * exl.GetType().GetRuntimeMethod("Execute", new[] { typeof(object[]) }), * argexparray); * } * return Expression.Constant(0);*/ } }