Skip to content
Snippets Groups Projects
SOMDocToLambdaExpression.cs 42.8 KiB
Newer Older
  • Learn to ignore specific revisions
  • using System.Collections.Generic;
    using System.Linq.Expressions;
    using System;
    using System.Linq;
    using UnityEngine;
    
    using System.Reflection;
    
    using UnityEditor;
    using System.Collections.ObjectModel;
    
    using Unity.Burst.Intrinsics;
    
    
    namespace REST_JSON_API
    {
        abstract public partial class SOMDoc
        {
    
            public Func<object[], object[]> PartialInvokeCastingLambdaExpression(out Expression compile_base, out Type[] signature, object[] callArgs = null, bool[] useArgs = null)
    
    MaZiFAU's avatar
    MaZiFAU committed
            {
                List<Type> signature_list;
                LambdaExpression lambda_orig = GetLambdaExpression();
    
                if (FuncExtensions.IsFuncType(lambda_orig.ReturnType, out int signature_count))
                {
                    signature_list = lambda_orig.ReturnType.GetGenericArguments().ToList();
    
    MaZiFAU's avatar
    MaZiFAU committed
                }
                else
                {
                    signature_count = lambda_orig.Parameters.Count + 1;
                    signature_list =
                        Enumerable.Append(
                            lambda_orig.Parameters.Select(p => p.Type),
                            lambda_orig.ReturnType)
                        .ToList();
    
    MaZiFAU's avatar
    MaZiFAU committed
                }
    
                ParameterExpression object_arr = Expression.Parameter(typeof(object[]), "PARAMS_Arr");
                Expression[] cast_new_to_signature = new Expression[signature_count - 1];
    
                List<int> removed = new();
                for (int i = 0; i < signature_count - 1; i++)
                {
                    if (callArgs != null && callArgs.Length < i
                     && (useArgs == null || (useArgs.Length < i && useArgs[i])))
                    {
                        cast_new_to_signature[i] =
                            Expression.Constant(callArgs[i], signature_list[i]);
    
                        removed.Add(i);
                        continue;
                    }
    
                    cast_new_to_signature[i] =
                        Expression.Convert(
                            Expression.ArrayIndex(
                                object_arr,
                                Expression.Constant(i)
                            ),
                            signature_list[i]
                        );
                }
                signature = signature_list.SkipAt(removed).ToArray();
    
                LambdaExpression final_expression =
                    Expression.Lambda(
                        typeof(object[]).IsAssignableFrom(signature[^1])
    
                        ? Expression.Invoke(compile_base, cast_new_to_signature)
    
    MaZiFAU's avatar
    MaZiFAU committed
                        : Expression.NewArrayInit(
                            typeof(object),
    
                            new Expression[] { Expression.Convert(Expression.Invoke(compile_base, cast_new_to_signature), typeof(object)) }),
    
    MaZiFAU's avatar
    MaZiFAU committed
    
                        object_arr
                    );
    
                return final_expression.Compile() as Func<object[], object[]>;
            }
    
            protected static class SOMDocToLambdaExpression
    
            {
                // TODO: Populate Dictionaries
                #region ExpressionDictionaries
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public 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,
    
    MaZiFAU's avatar
    MaZiFAU committed
                    MakeRoot },
    
                { MMTConstants.Tuple,
                    MakeTupel },
    
                { MMTConstants.Product, // TODO:Automate
                    MakeTupel },
    
                { MMTConstants.MakeObjectArray,
                    MakeObjArray },
    
                    MakeInstantList },
                { MMTConstants.ListEnd,
                    MakeListEnd },
                { MMTConstants.ListLiteral,
                    InsertFrontListLiteral },
                { MMTConstants.ListType,
                    Identity0 },
    
                { MMTConstants.ProjL,
    
                { MMTConstants.ProjR,
    
                    ProjRVecTupel },
                { MMTConstants.Map,
                    ChainMakes(new[]{
                        CallAnyFunction(false, "Select", typeof(Enumerable)),
    
                        CallAnyFunction(false, "ToList", typeof(Enumerable))})},
    
                { MMTConstants.FeedForwardWhile,
    
    MaZiFAU's avatar
    MaZiFAU committed
                    FeedForwardUntil },
                { MMTConstants.FeedForwardWhile2,
                    FeedForwardUntil },
    
                { MMTConstants.PartialAggregate,
                    CallAnyFunction(false, "PartialAggregate", typeof(IEnumerableExtensions)) },
                { MMTConstants.ToArray,
                    CallAnyFunction(false, "ToArray", typeof(Enumerable)) },
    
    MaZiFAU's avatar
    MaZiFAU committed
                { MMTConstants.Filter2,
                    ChainMakes(new[]{
    
                        CallAnyFunction(false, "Where", typeof(Enumerable)),
    
    MaZiFAU's avatar
    MaZiFAU committed
                        CallAnyFunction(false, "ToList", typeof(Enumerable))})},
                { MMTConstants.Fold,
                    Aggregate},
    
                { MMTConstants.PropertyX,
                    GetPropertyOrField("x") },
                { MMTConstants.PropertyY,
                    GetPropertyOrField("y") },
                { MMTConstants.PropertyZ,
                    GetPropertyOrField("z") },
    
                { MMTConstants.IndexList,
                    IntCastedIndexer("Item") },
    
    MaZiFAU's avatar
    MaZiFAU committed
                { MMTConstants.IfThenElse,
                    IfThenElse },
                { MMTConstants.GetField,
                    GetPropertyOrFieldDynamic },
    
            };
    
                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},
    
                    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},
    
                    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},
    
                { MMTConstants.MinusRealLit,
                    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 (FactRecorder.AllFacts.TryGetValue(URI, out Fact fact))
                    {
    
                        Expression lambda_lambda;
                        int ll_params_c;
                        IEnumerable<Type> ll_params;
    
                        if (fact is not FunctionFact ffact)
                        {
                            MMTFact decl = fact.MakeMMTDeclaration();
                            SOMDoc df = decl is MMTGeneralFact gf
                                ? gf.defines
                                : (decl as MMTValueFact).lhs;
    
                            LambdaExpression lambda_orig = df.GetLambdaExpression();
                            if (lambda_orig.Body is not LambdaExpression lambda_lambda1
                             || lambda_applicant.Length == 0)
                                return lambda_orig;
    
                            lambda_lambda = lambda_lambda1;
                            ll_params_c = lambda_lambda1.Parameters.Count();
                            ll_params = lambda_lambda1.Parameters.Select(p => p.Type);
                        }
                        else
                        {
                            lambda_lambda = ffact.LambdaExpression;
                            ll_params_c = ffact.Signature.Length - 1;
                            ll_params = ffact.Signature.Take(ll_params_c);
                        }
    
                        int free_params = ll_params_c - lambda_applicant.Length;
    
                        if (free_params <= 0)
                            return Expression.Lambda(
    
                                    lambda_lambda,
                                    lambda_applicant.Select(app => app.Body).Take(ll_params_c)),
    
                                found_bound_params
                            );
    
                        ParameterExpression[] new_params =
    
                            .Skip(lambda_applicant.Length)
    
                            .ToArray();
    
                        return Expression.Lambda(
                            Expression.Lambda(
                                Expression.Invoke(
                                    lambda_lambda,
                                    lambda_applicant.Select(app => app.Body).AppendRange(new_params)
                                ),
                                new_params
                            ),
                            found_bound_params
                        );
    
                    if (MMTtoUnaryExpressionType.TryGetValue(URI, out var unnary_type))
                    {
    
                            ThrowArgumentException(unnary_type, 1);
    
    
                        Type UnarySecondArgument = lambda_applicant.Count() < 2 ? null : lambda_applicant[1].ReturnType;
    
    
                        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 + "\"");
                }
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static CustomFunction ChainMakes(CustomFunction[] makes)
    
                    => (LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) =>
                    {
                        foreach (var make in makes)
                            lambda_applicant = new[] { make(lambda_applicant, lambda_arguments, bound_params) };
    
                        return lambda_applicant[0];
                    };
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public 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);
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression ParseFuncUUToExpression<U>(Func<U, U> func)
    
                    => (Expression<Func<U, U>>)((U x) => func(x));
    
    
                public static LambdaExpression MakeInvert(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                    => Expression.Lambda(
                            Expression.MakeBinary(ExpressionType.Divide,
                                Expression.Convert(Expression.Constant(1), lambda_applicant[0].ReturnType),
                                lambda_applicant[0].Body),
                            bound_params
                        );
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public 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
                        );
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public 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
                        );
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression MakeRoot(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                    => ExpresionFuncToLambda(
                              lambda_applicant[0].ReturnType == typeof(float) ? ParseFuncUUToExpression<float>(MathF.Sqrt)
                            : lambda_applicant[0].ReturnType == typeof(double) ? ParseFuncUUToExpression<double>(Math.Sqrt)
                            : throw new NotImplementedException("Sqrt for " + lambda_applicant[0].ReturnType),
    
                            "Sqrt", lambda_arguments.Length > 0 ? lambda_arguments : lambda_applicant, bound_params, 1
                        );
    
                public static LambdaExpression ProjLVecTupel(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                {
                    if (lambda_applicant[0].Body is MethodCallExpression meth
                     && meth.Method.DeclaringType == typeof(Tuple)
                     && meth.Method.Name == "Create") // Access Values Directly
                    {
                        return Expression.Lambda(meth.Arguments.First(), bound_params);
                    }
    
                    return (
                            lambda_applicant[0].ReturnType == typeof(Vector3)
                            ? GetPropertyOrField("x")
                            : GetPropertyOrField("Item1")
                            )
                            (lambda_applicant, lambda_arguments, bound_params);
                }
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression ProjRVecTupel(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                    LambdaExpression[] Items_applicant;
    
                    if (lambda_applicant[0].Body is MethodCallExpression meth
                     && meth.Method.DeclaringType == typeof(Tuple)
                     && meth.Method.Name == "Create") // Access Values Directly
                    {
                        Items_applicant = meth.Arguments
                            .Skip(1)
                            .Select(arg => Expression.Lambda(arg, bound_params))
    
                    }
                    else
                    {
                        Items_applicant =
                            lambda_applicant[0].ReturnType == typeof(Vector3)
                            ? new string[] { "y", "z" }
                                .Select(prop => GetPropertyOrField(prop)(lambda_applicant, lambda_arguments, bound_params))
                                .ToArray()
                            : 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 Items_applicant.Length == 1
                        ? Items_applicant[0]
                        : MakeTupel(Items_applicant, lambda_arguments, bound_params);
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression MakeTupel(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
                        );
    
    
                    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
                    );
                }
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression MakeObjArray(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                {
                    if (lambda_applicant.Length == 1
                     && lambda_applicant[0].ReturnType.IsArray
                     && !typeof(object[]).IsAssignableFrom(lambda_applicant[0].ReturnType))
                    {
                        ParameterExpression lambda_param =
                            Expression.Parameter(lambda_applicant[0].ReturnType.GetElementType());
    
                        return CallAnyFunction(false, "ToArray", typeof(Enumerable))(
                            new[] {
                                CallAnyFunction(false, "Select", typeof(Enumerable),
                                    new[] { (1u, Expression.Lambda(Expression.Lambda(Expression.Convert(lambda_param, typeof(object)), lambda_param))) }
                                ) (lambda_applicant, lambda_arguments, bound_params)
                            },
                            lambda_arguments,
    
                                Expression.NewArrayInit(
                                    typeof(object),
                                    lambda_applicant.Select(l => Expression.Convert(l.Body, typeof(object)))
                                ),
                                bound_params
                            );
    
    MaZiFAU's avatar
    MaZiFAU committed
                public 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_applicant.Select(l => l.Body) // Expression.Convert(l.Body, lambda_applicant[0].ReturnType))
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression InsertFrontListLiteral(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                    => Expression.Lambda(
                            Expression.Call(
    
                                lambda_applicant[0].Body,
    
                                lambda_applicant[0].ReturnType.GetMethod("Insert"),
    
                                Expression.Constant(0, typeof(int)),
                                lambda_applicant[1].Body
                            ),
                            bound_params
                        );
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression MakeListEnd(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                    => Expression.Lambda(
    
                            Expression.New(typeof(List<>).MakeGenericType(lambda_applicant[0].ReturnType))
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression Identity0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                    => lambda_applicant[0];
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression Index0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                    => Expression.Lambda(
                            Expression.ArrayIndex(lambda_applicant[0].Body, Expression.Constant(0))
                        );
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public 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);
                }
    
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression IfThenElse(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                    => Expression.Lambda( // lambda_applicant[0] is ReturnType
                            Expression.Condition(lambda_applicant[1].Body, lambda_applicant[2].Body, lambda_applicant[3].Body),
                            bound_params
                        );
    
                public static LambdaExpression Aggregate(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                {
                    lambda_applicant[2] = // switch order of parameters
                        Expression.Lambda(
                            Expression.Lambda(
                                ((LambdaExpression)lambda_applicant[2].Body).Body,
                                ((LambdaExpression)lambda_applicant[2].Body).Parameters.Reverse()
                            ),
                            lambda_applicant[2].Parameters
                        );
    
                    return CallAnyFunction(false, "Aggregate", typeof(Enumerable))
                        (lambda_applicant, lambda_arguments, bound_params);
                }
    
                public static LambdaExpression FeedForwardUntil(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                    => ChainMakes(new[]{
                           CallAnyFunction(false, "FeedForwardUntil", typeof(IEnumerableExtensions)),
                           CallAnyFunction(false, "ToList", typeof(Enumerable))}
                    )(lambda_applicant[1..], lambda_arguments, bound_params);  // lambda_applicant[0] is ReturnType
    
                public 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)
    
                            (MethodInfo m, bool Success, Dictionary<string, (Type To, int Dirty)> method_types, Expression[] partials)[] recipe =
    
                                type.GetMethods()
                                .Where(m =>
                                    m.Name.Equals(method_name) &&
                                    m.GetParameters().Length == call_args.Length
                                ).Select(m =>
                                {
                                    Dictionary<string, (Type To, int Dirty)> method_types = new();
    
                                    (bool Success, Dictionary<string, (Type To, int Dirty)> GenericParameters, Expression partials)[] test =
                                        m.GetParameters()
                                        .Zip(lambda_args_new, (par, lamb) =>
                                            IsCompatibleType(par.ParameterType, lamb.ReturnType, lamb.Body, 0))
                                        .ToArray();
    
                                    if (!test.All(t => t.Success)
                                     || !test.All(t => method_types.TryAddAllFrom(t.GenericParameters)))
    
                                        return (m, false, new(), new LambdaExpression[0]);
    
                                        return (m, Success: true, method_types, test.Select(t => t.partials).ToArray());
    
                                }).Where(t => t.Success)
                                .ToArray();
    
                            if (recipe.Length == 0)
    
                                throw new Exception($"Could not find method \"{method_name}\" in Type \"{type}\" and Type Signature [{string.Join(", ", lambda_args_type.Select(o => o.ToString()))}]");
    
                            if (recipe.Length > 1) // AmbiguousMatchException
    
                                Debug.LogWarning($"Found methods not unique for \"{method_name}\" in Type \"{type}\" and Type Signature [{string.Join(", ", lambda_args_type.Select(o => o.ToString()))}]");
    
                            call_args = recipe[0].partials;
    
                            if (call_method.IsGenericMethod)
                            {
    
                                call_method = call_method.MakeGenericMethod(
                                    call_method.GetGenericArguments()
                                    .Select(arg => recipe[0].method_types[arg.Name].To)
                                    .ToArray()
                                );
                            }
    
                        }
    
                        return Expression.Lambda(
                            self
                                ? Expression.Call(lambda_applicant[0].Body, call_method, call_args)
                                : Expression.Call(call_method, call_args),
                            bound_params
                        );
    
    
                        static
                        (bool Success, Dictionary<string, (Type To, int Dirty)> GenericParameters, Expression partials)
                        IsCompatibleType(Type generic, Type from_me, Expression source_from_me, int dirty)
                        {
                            Dictionary<string, (Type To, int Dirty)> retDic = new();
    
                            if (generic.HasElementType
                             && from_me.HasElementType)
                            {
                                generic = generic.GetElementType();
                                from_me = from_me.GetElementType();
                            }
    
                            if (generic.IsAssignableFrom(from_me))
                                return (true, new(), source_from_me);
    
                            if (generic.IsGenericType)
                            {
                                (bool Success, Dictionary<string, (Type To, int Dirty)> GenericParameters, Expression partials)[] recursion;
    
                                if (generic.IsInterface
                                && !from_me.IsInterface)
                                {
                                    if (generic.Equals(typeof(IEnumerable<>))
                                     && from_me.IsArray) // GetInterfaces() drops here the generic type?
                                        return IsCompatibleType(
                                            generic.GetGenericArguments()[0],
                                            from_me.GetElementType(),
                                            source_from_me, //will not be Func[], atmost(?) List<Func>
                                            dirty
                                        );
    
                                    recursion = generic.GetInterfaces().Select(gi =>
                                            from_me.GetInterfaces()
                                            .Select(fi => IsCompatibleType(gi, fi, source_from_me, dirty))
                                            .FirstOrDefault(t => t.Success)
                                        ).ToArray();
                                }
                                else
                                {
                                    if (FuncExtensions.IsFuncType(generic, out int gene_sig)
                                     && FuncExtensions.IsFuncType(from_me, out int from_sig)
                                     && from_me == source_from_me.Type) // from_me <within_or_equal> source_from_me.Type
                                    { // lets pretend C# uses lambda logic like MMT
                                        if (gene_sig > from_sig)
                                            return (false, new(), source_from_me);
    
                                        bool IsDirty = gene_sig != from_sig;
                                        if (IsDirty)
                                            dirty++;
    
                                        bool IsParame = source_from_me is ParameterExpression;
                                        bool IsLambda = source_from_me is LambdaExpression;
                                        if (!IsParame && !IsLambda)
                                        {
                                            Debug.LogWarning($"Unexpected Expression for Parameter \"{nameof(source_from_me)}\"");
                                            if (IsDirty)
                                                return (false, new(), source_from_me);
                                            else
                                                goto GeneralGenericCase;
                                        }
    
                                        Expression[] expr_decomp = null;
                                        if (IsParame)
                                        {
    
                                            if (IsDirty)
                                            {
                                                Debug.LogWarning(
                                                    "If you see this message, you have found an example for this branch.\n" +
                                                    "I have no clue what will happen now. Good Luck!");
    
                                                // What should it be?
                                                // Call(generic)([(from_me)=>generic](from_me))
                                                // Call([(A)=>C]=>...)([([(a,b)=>c]=>...)=>[(A)=>C]=>...]([(a,b)=>c]=>...))
                                                // [(from_me-generic)=>Call(generic)([(from_me)=>generic](from_me))]
                                                //
                                                //generic: [(A)=>C]=>...
                                                //from_me: [(a,b)=>c]=>...
                                                //result:  [(A)=>[(a,b)=>c](b)]=>...
                                                //   or?:  [(a)=>[(b)=>c]]=>...
                                                //   or?:  [[(a,b)=>c]=>C]=>...
                                                //
                                                //(from_me)=>generic]:
                                                //  [[(a,b)=>c]=>...]=>[[(A)=>C]=>...]
    
                                                //Lamb(Call(from_me, (A,b)), A) -> escalate b
                                            }
    
    
                                            Type[] from_args = from_me.GetGenericArguments();
                                            Type partial_type = generic
                                                .GetGenericTypeDefinition()
    
                                                .MakeGenericType(!IsDirty
                                                    ? from_args
                                                    : from_args[..(gene_sig - 1)]
    
                                                    .Append(FuncExtensions.CreateFuncType(from_args[(gene_sig - 1)..]))
                                                    .ToArray()
                                                );
    
                                                partial_type.GetGenericArguments()
                                                .Select(typ => Expression.Parameter(typ))
                                                .ToArray();
                                        }
                                        else
                                        if (IsLambda)
                                        {
                                            LambdaExpression source_lambda = source_from_me as LambdaExpression;
    
                                            ReadOnlyCollection<ParameterExpression> from_para = source_lambda.Parameters;
                                            expr_decomp =
                                                from_para.Take<Expression>(gene_sig - 1)
                                                .Append(!IsDirty
                                                    ? source_lambda.Body
                                                    : Expression.Lambda(source_lambda.Body, from_para.Skip(gene_sig - 1)))
                                                .ToArray();
                                        }
    
                                        recursion =
                                           generic.GetGenericArguments()
                                           .Zip(expr_decomp, (par, expr) => IsCompatibleType(par, expr.Type, expr, dirty))
                                           .ToArray();
    
                                        if (!recursion.All(t => t.Success)
                                         || !recursion.All(t => retDic.TryAddAllFrom(t.GenericParameters)))
                                            return (false, new(), source_from_me);
    
                                        Expression source_partial =
    
                                                Expression.Parameter(
                                                    FuncExtensions.CreateFuncType(
                                                        recursion.Select(t => t.partials.Type).ToArray())) :
                                            IsLambda ?
                                                Expression.Lambda(
                                                    recursion[^1].partials,
                                                    recursion[..^1].Select(t => t.partials as ParameterExpression)
                                                ) :
                                        null; // For compiler
    
                                        return (true, retDic, source_partial);
                                    }
    
                                GeneralGenericCase:
    
                                    if (!from_me.IsGenericType
                                     || !generic.GetGenericTypeDefinition().Equals(from_me.GetGenericTypeDefinition()))
                                        return (false, new(), source_from_me);
    
                                    recursion =
                                       generic.GetGenericArguments()
                                       .Zip(from_me.GetGenericArguments(), (par, typ) => IsCompatibleType(par, typ, source_from_me, dirty))
                                       .ToArray();
                                }
    
                                if (!recursion.All(t => t.Success)
                                 || !recursion.All(t => retDic.TryAddAllFrom(t.GenericParameters)))
                                    return (false, new(), source_from_me);
                                else
                                    return (true, retDic, source_from_me);
                            }
    
                            if (generic.IsGenericParameter)
                            { // TODO? .GetGenericParameterConstraints()
                                return (true, new() { { generic.Name, (from_me, dirty) } }, source_from_me);
                            }
    
                            return (false, new(), source_from_me);
                        }
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static LambdaExpression MakeType(KeyValuePair<string, LambdaExpression>[] members, ParameterExpression[] bound_params)
                    => Expression.Lambda(
                            TupleFactory
                                .Create(members.Select(m => new KeyValuePair<string, Type>(m.Key, m.Value.ReturnType)))
                                .MakeNewExpression(members.Select(m => m.Value.Body)),
                            bound_params
                        );
    
                public static LambdaExpression GetPropertyOrFieldDynamic(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                {
                    if (lambda_applicant[1].Compile() is not Func<string> name_gen)
                        throw new ArgumentException($"Second element of {nameof(lambda_applicant)} hast to resolve to {typeof(string)}, argumentless!");
    
                    return GetPropertyOrField(name_gen())
                        (lambda_applicant.Where((l, i) => i != 1).ToArray(), lambda_arguments, bound_params);
                }
    
                public static CustomFunction GetPropertyOrField(string property_name)
    
                    => (LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
    
                    => lambda_applicant.Length == 1
                        ? Expression.Lambda(
    
                            Expression.PropertyOrField(lambda_applicant[0].Body, property_name),
                            bound_params
    
    MaZiFAU's avatar
    MaZiFAU committed
                            Expression.Property(lambda_applicant[0].Body, property_name,
                                lambda_applicant.Skip(1).Select(l => l.Body).ToArray()),
    
    MaZiFAU's avatar
    MaZiFAU committed
                public static CustomFunction IntCastedIndexer(string property_name)
    
                    => (LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
                    => Expression.Lambda(
    
    MaZiFAU's avatar
    MaZiFAU committed
                            Expression.Property(lambda_applicant[0].Body, property_name,
                                lambda_applicant.Skip(1).Select(l => Expression.Convert(l.Body, typeof(int))).ToArray()),