using System.Collections.Generic; using System.Linq.Expressions; using System; using System.Linq; using UnityEngine; using System.Reflection; using MoreLinq; namespace REST_JSON_API { abstract public partial class SOMDoc { protected static class SOMDocToLambdaExpression<T> { // TODO: Populate Dictionaries #region ExpressionDictionaries delegate LambdaExpression CustomFunction(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params); private static readonly Dictionary<string, CustomFunction> MMTtoLambdaMaker = new() { { MMTConstants.Sin, MakeSin }, { MMTConstants.Cos, MakeCos }, { MMTConstants.SquareRoot, MakeCos }, { MMTConstants.Tuple, MakeTupel }, { MMTConstants.MakeObjectArray, MakeObjArray }, { "InstantList", MakeInstantList }, { MMTConstants.ListEnd, MakeListEnd }, { MMTConstants.ListLiteral, InsertFrontListLiteral }, { MMTConstants.ListType, Identity0 }, { MMTConstants.ProjL, GetPropertyOrField("Item1") }, { MMTConstants.ProjR, ProjRTupel }, { MMTConstants.FeedForwardWhile, CallAnyFunction(false, "FeedForwardWhile", typeof(IEnumerableExtensions)) }, { MMTConstants.PartialAggregate, CallAnyFunction(false, "PartialAggregate", typeof(IEnumerableExtensions)) }, { MMTConstants.ToArray, CallAnyFunction(false, "ToArray", typeof(Enumerable)) }, { MMTConstants.PropertyX, GetPropertyOrField("x") }, { MMTConstants.PropertyY, GetPropertyOrField("y") }, { MMTConstants.PropertyZ, GetPropertyOrField("z") }, { MMTConstants.TupelToVector, TupelToVector }, }; private static readonly Dictionary<string, ExpressionType> MMTtoBinaryExpressionType = new() { { MMTConstants.AddRealLit, ExpressionType.Add}, { "AddAssign", ExpressionType.AddAssign}, { "AddAssignChecked", ExpressionType.AddAssignChecked}, { "AddChecked", ExpressionType.AddChecked}, { "And", ExpressionType.And}, { "AndAlso", ExpressionType.AndAlso}, { "AndAssign", ExpressionType.AndAssign}, { "Assign", ExpressionType.Assign}, { MMTConstants.Divide, ExpressionType.Divide}, { "DivideAssign", ExpressionType.DivideAssign}, { "Equal", ExpressionType.Equal}, { "ExclusiveOr", ExpressionType.ExclusiveOr}, { "ExclusiveOrAssign", ExpressionType.ExclusiveOrAssign}, { "GreaterThan", ExpressionType.GreaterThan}, { "GreaterThanOrEqual", ExpressionType.GreaterThanOrEqual}, { "LeftShift", ExpressionType.LeftShift}, { "LeftShiftAssign", ExpressionType.LeftShiftAssign}, { MMTConstants.LessThan, ExpressionType.LessThan}, { "LessThanOrEqual", ExpressionType.LessThanOrEqual}, { "Modulo", ExpressionType.Modulo}, { "ModuloAssign", ExpressionType.ModuloAssign}, { MMTConstants.TimesRealLit, ExpressionType.Multiply}, { "MultiplyAssign", ExpressionType.MultiplyAssign}, { "MultiplyAssignChecked", ExpressionType.MultiplyAssignChecked}, { "MultiplyChecked", ExpressionType.MultiplyChecked}, { "NotEqual", ExpressionType.NotEqual}, { "Or", ExpressionType.Or}, { "OrAssign", ExpressionType.OrAssign}, { "OrElse", ExpressionType.OrElse}, { "Power", ExpressionType.Power}, { "PowerAssign", ExpressionType.PowerAssign}, { "RightShift", ExpressionType.RightShift}, { "RightShiftAssign", ExpressionType.RightShiftAssign}, { MMTConstants.Subtract, ExpressionType.Subtract}, { "SubtractAssign", ExpressionType.SubtractAssign}, { "SubtractAssignChecked", ExpressionType.SubtractAssignChecked}, { "SubtractChecked", ExpressionType.SubtractChecked}, }; private static readonly Dictionary<string, ExpressionType> MMTtoUnaryExpressionType = new() { //{ "Constant", // Not Unary // ExpressionType.Constant}, { "Convert", ExpressionType.Convert}, { "ConvertChecked", ExpressionType.ConvertChecked}, { "Decrement", ExpressionType.Decrement}, { "Increment", ExpressionType.Increment}, { "Negate", ExpressionType.Negate}, { "NegateChecked", ExpressionType.NegateChecked}, { "Not", ExpressionType.Not}, { "OnesComplement", ExpressionType.OnesComplement}, { "PostDecrementAssign", ExpressionType.PostDecrementAssign}, { "PostIncrementAssign", ExpressionType.PostIncrementAssign}, { "PreDecrementAssign", ExpressionType.PreDecrementAssign}, { "PreIncrementAssign", ExpressionType.PreIncrementAssign}, { "UnaryPlus", ExpressionType.UnaryPlus}, }; #endregion ExpressionDictionaries //TODO: case ((f->x)->y) instead of assumed (f->(x->y)) public static LambdaExpression MakeLambdaExpression(string URI, LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { void ThrowArgumentException(ExpressionType expression_cast, int expected) { throw new ArgumentException(string.Format( "\"Wrong number of Arguments. Required: {2}. Supplied: {3}.\\n\\tFor URI:\\\"{0}\\\"\\n\\tmapped to:\\\"{1}\\\"\"", URI, expression_cast, expected, lambda_applicant.Count() )); } ParameterExpression[] lambda_params = lambda_applicant .SelectMany(l => l.Parameters) .ToArray(); //PERF: .ToList().Sort() => .BinarySearch; //Too much overhead? ParameterExpression[] found_bound_params = bound_params .Where(p => lambda_params.Contains(p)) .ToArray(); if (MMTtoUnaryExpressionType.TryGetValue(URI, out var unnary_type)) { if (found_bound_params.Count() < 1) ThrowArgumentException(unnary_type, 1); Type UnarySecondArgument = found_bound_params.Count() < 2 ? null : found_bound_params[1].Type; return Expression.Lambda(Expression.MakeUnary(unnary_type, lambda_applicant[0].Body, UnarySecondArgument), found_bound_params); } else if (MMTtoBinaryExpressionType.TryGetValue(URI, out var binary_type)) { if (lambda_applicant.Count() != 2) ThrowArgumentException(binary_type, 2); return Expression.Lambda(Expression.MakeBinary(binary_type, lambda_applicant[0].Body, lambda_applicant[1].Body), found_bound_params); } else if (MMTtoLambdaMaker.TryGetValue(URI, out CustomFunction lamda_maker)) { return lamda_maker(lambda_applicant, lambda_arguments, found_bound_params); } else if (MMTConstants.OMS_TO_TYPE.TryGetValue(URI, out Type type)) { return Expression.Lambda(Expression.Default(type), null); } throw new NotImplementedException("Could not map URI: \"" + URI + "\""); } private static LambdaExpression ExpresionFuncToLambda(LambdaExpression func, string name, LambdaExpression[] args_lamda, ParameterExpression[] bound_params, uint nTargs_fallback) => Expression.Lambda(Expression.Invoke(func, args_lamda.Select(l => l.Body)), name, bound_params); private static LambdaExpression ParseFuncUUToExpression<U>(Func<U, U> func) => (Expression<Func<U, U>>)((U x) => func(x)); private static LambdaExpression MakeSin(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => ExpresionFuncToLambda( lambda_applicant[0].ReturnType == typeof(float) ? ParseFuncUUToExpression<float>(MathF.Sin) : lambda_applicant[0].ReturnType == typeof(double) ? ParseFuncUUToExpression<double>(Math.Sin) : throw new NotImplementedException("Sinus for " + lambda_applicant[0].ReturnType), "Sin", lambda_arguments.Length > 0 ? lambda_arguments : lambda_applicant, bound_params, 1 ); private static LambdaExpression MakeCos(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => ExpresionFuncToLambda( lambda_applicant[0].ReturnType == typeof(float) ? ParseFuncUUToExpression<float>(MathF.Cos) : lambda_applicant[0].ReturnType == typeof(double) ? ParseFuncUUToExpression<double>(Math.Cos) : throw new NotImplementedException("Cosinus for " + lambda_applicant[0].ReturnType), "Cos", lambda_arguments.Length > 0 ? lambda_arguments : lambda_applicant, bound_params, 1 ); private static LambdaExpression ProjRTupel(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { LambdaExpression[] Items_applicant = lambda_applicant[0].ReturnType.GetProperties() .OrderBy(fi => fi.Name) .Skip(1) // Item1 .Select(fi => Expression.Lambda( Expression.Property(lambda_applicant[0].Body, fi), bound_params)) .ToArray(); return MakeTupel(Items_applicant, lambda_arguments, bound_params); } private static LambdaExpression MakeTupel(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { Type[] genericTypes = new Type[lambda_applicant.Length]; for (int i = 0; i < lambda_applicant.Length; i++) genericTypes[i] = Type.MakeGenericMethodParameter(i); MethodInfo create = typeof(Tuple) .GetMethod("Create", genericTypes) .MakeGenericMethod(lambda_applicant.Select(l => l.ReturnType).ToArray()); return Expression.Lambda( Expression.Call(create, lambda_applicant.Select(l => l.Body)), bound_params ); } private static LambdaExpression TupelToVector(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { if (lambda_applicant.Length == 3 && lambda_applicant.All(l => l.ReturnType == typeof(float))) return ExpresionFuncToLambda( (Expression<Func<float, float, float, Vector3>>)((x, y, z) => new Vector3(x, y, z)), "UnityEngineVector3", lambda_applicant, bound_params, 3 ); else return ExpresionFuncToLambda( (Expression<Func<Tuple<float, float, float>, Vector3>>) (tupel => new Vector3(tupel.Item1, tupel.Item2, tupel.Item3)), "TupelToUnityEngineVector3", lambda_applicant, bound_params, 1 ); } private static LambdaExpression MakeObjArray(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda( Expression.NewArrayInit( typeof(object), lambda_applicant.Select(l => Expression.Convert(l.Body, typeof(object))) ), bound_params ); private static LambdaExpression MakeInstantList(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda( Expression.ListInit( Expression.New(typeof(List<>).MakeGenericType(lambda_applicant[0].ReturnType)), lambda_arguments.Select(l => l.Body) // Expression.Convert(l.Body, lambda_applicant[0].ReturnType)) ), bound_params ); private static LambdaExpression InsertFrontListLiteral(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda( Expression.Call( lambda_applicant[0].ReturnType.GetMethod("Insert"), lambda_applicant[0].Body, Expression.Constant(0, typeof(int)), lambda_applicant[1].Body ), bound_params ); private static LambdaExpression MakeListEnd(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda( Expression.New(typeof(List<>).MakeGenericType(lambda_applicant[0].ReturnType)) ); private static LambdaExpression Identity0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => lambda_applicant[0]; private static LambdaExpression Index0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda( Expression.ArrayIndex(lambda_applicant[0].Body, Expression.Constant(0)) ); private static LambdaExpression Tail(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) { LambdaExpression enumerated = CallAnyFunction(false, "Skip", typeof(Enumerable), new[] { (1u, Expression.Lambda(Expression.Constant(1, typeof(int)))) } )(lambda_applicant, lambda_arguments, bound_params); return CallAnyFunction(false, "ToArray", typeof(Enumerable))(new[] { enumerated }, null, bound_params); } private static CustomFunction CallAnyFunction(bool self, string method_name, Type type = null, (uint, LambdaExpression)[] lambda_manual = null) => (LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => { type ??= lambda_applicant[0].ReturnType; LambdaExpression[] lambda_args_new = lambda_applicant .Skip(self ? 1 : 0) .AppendRangeAt(lambda_manual) .ToArray(); Expression[] call_args = lambda_args_new .Select(l => l.Body) .ToArray(); Type[] lambda_args_type = lambda_args_new .Select(l => l.ReturnType) .ToArray(); MethodInfo call_method = // type.method_name type.GetMethod(method_name, lambda_args_type); if (call_method == null) { call_method = type.GetMethod(method_name); if (call_method == null) throw new Exception($"Could not find method \"{method_name}\" in Type \"{type}\" and Type Signature [{lambda_args_type}]"); if (call_method.IsGenericMethod) { Type[] distinct_types = lambda_args_type .SelectMany(l => !l.IsGenericType ? new Type[] { l } : l.GetGenericArguments()) .Distinct() .ToArray(); int n_generics = call_method.GetGenericArguments().Length; call_method = call_method.MakeGenericMethod(distinct_types[0..n_generics]); } } return Expression.Lambda( self ? Expression.Call(lambda_applicant[0].Body, call_method, call_args) : Expression.Call(call_method, call_args), bound_params ); }; private static CustomFunction GetPropertyOrField(string property_name) => (LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => Expression.Lambda( Expression.PropertyOrField(lambda_applicant[0].Body, property_name), bound_params ); } } }