Newer
Older
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
MaZiFAU
committed
using UnityEditor;
namespace REST_JSON_API
{
abstract public partial class SOMDoc
{
/// <summary>
/// translates this SOMDoc AST into a C\# AST, changes/casts the signature to Func<object[], object[]>, and compiles it.
/// Function arguments can be inserted at any position as to partially invoke the function.
/// </summary>
/// <param name="compile_base">Cached Expression Tree</param>
/// <param name="signature">the original signature</param>
/// <param name="callArgs">arguments to be inserted. The position carries over. Set <c>null</c> to skipp all.</param>
/// <param name="useArgs">set a position to <c>false</c> iff the corresponding entree in <paramref name="callArgs"/> shall be ignored / the original parameter be used.
/// Missing values will be interpreted as <c>true</c>. Set <c>null</c> to use all.</param>
/// <returns>the compiled function, with a new signature and partially inserted arguments.</returns>
MaZiFAU
committed
public Func<object[], object[]> PartialInvokeCastingLambdaExpression(out Expression compile_base, out Type[] signature, object[] callArgs = null, bool[] useArgs = null)
{
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
committed
compile_base = lambda_orig.Body;
}
else
{
signature_count = lambda_orig.Parameters.Count + 1;
signature_list =
Enumerable.Append(
lambda_orig.Parameters.Select(p => p.Type),
lambda_orig.ReturnType)
.ToList();
MaZiFAU
committed
compile_base = lambda_orig;
}
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])
MaZiFAU
committed
? Expression.Invoke(compile_base, cast_new_to_signature)
MaZiFAU
committed
new Expression[] { Expression.Convert(Expression.Invoke(compile_base, cast_new_to_signature), typeof(object)) }),
object_arr
);
return final_expression.Compile() as Func<object[], object[]>;
}
/// <summary>
/// Libary and configuartions to translate a SOMDoc AST into an Expression-Tree
/// </summary>
{
// TODO: Populate Dictionaries
#region ExpressionDictionaries
/// <summary>
/// Builds a <see cref="LambdaExpression"/> within <see cref="MakeLambdaExpression"/>
/// </summary>
/// <param name="lambda_applicant">arguments to be used; usually comes from <see cref="OMA.arguments"/></param>
/// <param name="lambda_arguments">currently unused. space for a second set of parameters</param>
/// <param name="bound_params">to be bound as parameters in <see cref="Expression.Lambda(Expression, ParameterExpression[])"/></param>
/// <returns>encoding the current Abstract-Syntax-Tree</returns>
public delegate LambdaExpression CustomFunction(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params);
private static readonly Dictionary<string, CustomFunction> MMTtoLambdaMaker = new()
{
MaZiFAU
committed
{ MMTConstants.InvertRealLit,
MakeInvert },
{ MMTConstants.Sin,
MakeSin },
{ MMTConstants.Cos,
MakeCos },
{ MMTConstants.SquareRoot,
{ MMTConstants.Tuple,
MakeTupel },
MaZiFAU
committed
{ MMTConstants.Product, // TODO:Automate
MakeTupel },
{ MMTConstants.Angle, // TODO: get AST from server?
CalculateAngle },
{ MMTConstants.MakeObjectArray,
MakeObjArray },
MaZiFAU
committed
{ MMTConstants.ListApplicant,
MakeInstantList },
{ MMTConstants.ListEnd,
MakeListEnd },
{ MMTConstants.ListLiteral,
InsertFrontListLiteral },
{ MMTConstants.ListType,
Identity0 },
{ MMTConstants.ScalarProduct,
CallAnyFunction(false, "Dot", typeof(Vector3)) },
{ MMTConstants.VecCross,
CallAnyFunction(false, "Cross", typeof(Vector3)) },
//{ MMTConstants.VecMultI,
// CallAnyFunction(false, "Scale", typeof(Vector3)) },
MaZiFAU
committed
ProjLVecTupel },
MaZiFAU
committed
ProjRVecTupel },
{ MMTConstants.Map,
ChainMakes(new[]{
CallAnyFunction(false, "Select", typeof(Enumerable)),
CallAnyFunction(false, "ToList", typeof(Enumerable))})},
{ MMTConstants.FeedForwardWhileT2,
FeedForwardUntil },
{ MMTConstants.PartialAggregate,
CallAnyFunction(false, "PartialAggregate", typeof(IEnumerableExtensions)) },
{ MMTConstants.ToArray,
CallAnyFunction(false, "ToArray", typeof(Enumerable)) },
{ MMTConstants.Filter,
ChainMakes(new[]{
CallAnyFunction(false, "Where", typeof(Enumerable)),
CallAnyFunction(false, "ToList", typeof(Enumerable))})},
MaZiFAU
committed
CallAnyFunction(false, "Where", typeof(Enumerable)),
{ MMTConstants.FilterT2,
ChainMakes(new[]{
CallAnyFunction(false, "Where", typeof(Enumerable)),
CallAnyFunction(false, "ToList", typeof(Enumerable))})},
{ MMTConstants.PropertyX,
GetPropertyOrField("x") },
{ MMTConstants.PropertyY,
GetPropertyOrField("y") },
{ MMTConstants.PropertyZ,
GetPropertyOrField("z") },
{ MMTConstants.IndexList,
IntCastedIndexer("Item") },
{ MMTConstants.IfThenElse,
IfThenElse },
{ MMTConstants.GetField,
GetPropertyOrFieldDynamic },
{ MMTConstants.Invoke,
PartialInvoke },
};
private static readonly Dictionary<string, ExpressionType> MMTtoBinaryExpressionType = new()
{
{ MMTConstants.PointAddI,
ExpressionType.Add},
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
{ "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.LEQRealLit,
ExpressionType.LessThanOrEqual},
{ "Modulo",
ExpressionType.Modulo},
{ "ModuloAssign",
ExpressionType.ModuloAssign},
{ MMTConstants.VecMultI,
ExpressionType.Multiply},
{ MMTConstants.RealMultiplication,
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))
/// <summary>
/// Maps <paramref name="URI"/> to an operation and uses the other parameters as operants to build an Expression-Tree.
/// </summary>
/// <param name="URI"><see cref="MMTConstants"/> to be mapped to an operation</param>
/// <param name="lambda_applicant">arguments to be used; usually comes from <see cref="OMA.arguments"/></param>
/// <param name="lambda_arguments">currently unused. space for a second set of parameters</param>
/// <param name="bound_params">to be bound as parameters in <see cref="Expression.Lambda(Expression, ParameterExpression[])"/></param>
/// <returns>encoding the current Abstract-Syntax-Tree</returns>
/// <exception cref="ArgumentException">iff <paramref name="lambda_applicant"/> could not be used in <paramref name="URI"/></exception>
/// <exception cref="NotImplementedException">iff <paramref name="URI"/> could not be mapped</exception>
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)
ParameterExpression[] found_bound_params =
bound_params
.Where(p => lambda_params.Contains(p))
.ToArray();
if (MMTtoUnaryExpressionType.TryGetValue(URI, out var unnary_type))
{
if (lambda_applicant.Count() < 1)
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 // Last entree to avoid stack-overflow:
if (FactRecorder.AllFacts.TryGetValue(URI, out Fact fact))
{
Expression lambda_orig =
Expression.Constant(fact.CompiledValue, type);
if (FuncExtensions.IsFuncType(type, out int signature_count))
{ // has to be LambdaExpression!
ParameterExpression[] invoke_params = type
.GetGenericArguments()
.Take(signature_count - 1)
.Select(t => Expression.Parameter(t))
.ToArray();
lambda_orig = Expression.Lambda(
Expression.Invoke(lambda_orig, invoke_params),
invoke_params
);
}
if (lambda_applicant.Length == 0)
return Expression.Lambda(lambda_orig, null);
else
return _PartialInvoke(lambda_orig, lambda_applicant, 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 + "\"");
}
#pragma warning disable IDE0060 // Nicht verwendete Parameter entfernen // Signatures given by CustomFunction
public static LambdaExpression PartialInvoke(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
=> _PartialInvoke(lambda_applicant[0].Body, lambda_arguments, bound_params);
/// <summary>
/// May invoke a function with an incomplete parameter set.
/// </summary>
/// <param name="func">function to be invoked</param>
/// <param name="lambda_arguments">arguments to invoke with</param>
/// <param name="bound_params">to be bound as parameters in <see cref="Expression.Lambda(Expression, ParameterExpression[])"/></param>
/// <returns>new function with up to <paramref name="lambda_arguments"/>.Count less arguments</returns>
/// <exception cref="ArgumentException">iff <paramref name="func"/> is not a function</exception>
public static LambdaExpression _PartialInvoke(Expression func, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
{
if (!FuncExtensions.IsFuncType(func.Type, out int signature_count))
throw new ArgumentException($"{nameof(func)} has to be Func");
int params_count = signature_count - 1;
IEnumerable<Type> params_type = func.Type.GetGenericArguments().Take(params_count);
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
int free_params = params_count - lambda_arguments.Length;
if (free_params <= 0)
return Expression.Lambda(
Expression.Invoke(
func,
lambda_arguments.Select(app => app.Body).Take(params_count)),
bound_params
);
ParameterExpression[] new_params =
params_type
.Skip(lambda_arguments.Length)
.Select(t => Expression.Parameter(t))
.ToArray();
return Expression.Lambda(
Expression.Lambda(
Expression.Invoke(
func,
lambda_arguments.Select(app => app.Body).AppendRange(new_params)
),
new_params
),
bound_params
);
}
/// <summary>
/// Chains multiple <see cref="CustomFunction"/>s into a single one. The resalt of one will be used as lambda_applicant for the next one.
/// </summary>
/// <param name="makes">to be chained</param>
/// <returns>wich combines all <paramref name="makes"/>. When executed it will return the return value of the last <see cref="CustomFunction"/> in <paramref name="makes"/>.</returns>
MaZiFAU
committed
=> (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];
};
public static LambdaExpression ExpresionFuncToLambda(LambdaExpression func, string name_me, LambdaExpression[] args_lamda, ParameterExpression[] bound_params)
=> Expression.Lambda(Expression.Invoke(func, args_lamda.Select(l => l.Body)), name_me, bound_params);
public static LambdaExpression ParseFuncUUToExpression<U>(Func<U, U> func)
=> (Expression<Func<U, U>>)((U x) => func(x));
MaZiFAU
committed
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
);
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
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
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
);
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
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))
MaZiFAU
committed
.ToArray();
}
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();
}
MaZiFAU
committed
return Items_applicant.Length == 1
? Items_applicant[0]
: MakeTupel(Items_applicant, lambda_arguments, bound_params);
public static LambdaExpression CalculateAngle(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
=> MakeTupel(new[] {
ExpresionFuncToLambda(
(Expression<Func<Vector3, Vector3, Vector3, float>>)((Vector3 a, Vector3 b, Vector3 c)
=> Mathf.Acos(Vector3.Dot((b - a).normalized, (b - c).normalized))),
"angle_between", lambda_applicant, bound_params
)},
null, bound_params
);
public static LambdaExpression MakeTupel(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
MaZiFAU
committed
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
MaZiFAU
committed
);
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
);
}
public static LambdaExpression MakeObjArray(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
MaZiFAU
committed
{
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,
MaZiFAU
committed
}
return Expression.Lambda(
Expression.NewArrayInit(
typeof(object),
lambda_applicant.Select(l => Expression.Convert(l.Body, typeof(object)))
),
bound_params
);
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)),
MaZiFAU
committed
lambda_applicant.Select(l => l.Body) // Expression.Convert(l.Body, lambda_applicant[0].ReturnType))
),
bound_params
);
public static LambdaExpression InsertFrontListLiteral(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
=> Expression.Lambda(
Expression.Call(
MaZiFAU
committed
lambda_applicant[0].ReturnType.GetMethod("Insert"),
Expression.Constant(0, typeof(int)),
lambda_applicant[1].Body
),
bound_params
);
public static LambdaExpression MakeListEnd(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
Expression.New(typeof(List<>).MakeGenericType(lambda_applicant[0].ReturnType))
public static LambdaExpression Identity0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
=> lambda_applicant[0];
public static LambdaExpression Index0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
=> Expression.Lambda(
Expression.ArrayIndex(lambda_applicant[0].Body, Expression.Constant(0))
);
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);
}
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
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
/// <summary>
/// Find (any) generic methods and call it, given the arguments.
/// This method tries to match the arguments to a method called <paramref name="method_name"/> in <paramref name="type"/>.
/// If unsuccessfull, an Exception will be thrown.
/// If the results are undecisive, the first one will be used and a log-entree created.
/// </summary>
/// <remarks>Although non-generic methods can be used this way, it is advised against this usage for performance reasons!</remarks>
/// <param name="self">if <c>true</c> the method will be called on the first argument itselfe; otherwise on <paramref name="type"/></param>
/// <param name="method_name">the name of the function to be called</param>
/// <param name="type">the <c>Type</c> the function is a member of, or <c>null</c> iff the type of the first argument is to be used</param>
/// <param name="lambda_manual">can be used to manually insert any number of constant arguments at any position (0-indexed)</param>
/// <returns>that dynamically builds a method call</returns>
/// <exception cref="Exception">Iff no matching method could be found</exception>
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) =>
{
LambdaExpression[] lambda_args_new = lambda_applicant
.AppendRangeAt(lambda_manual)
type ??= lambda_args_new[0].ReturnType;
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)
MaZiFAU
committed
{ // call_method is overloaded or generic
(MethodInfo m, bool Success, Dictionary<string, (Type To, int Dirty)> method_types, Expression[] partials)[] recipe =
MaZiFAU
committed
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]);
MaZiFAU
committed
else
return (m, Success: true, method_types, test.Select(t => t.partials).ToArray());
MaZiFAU
committed
}).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()))}]");
MaZiFAU
committed
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()))}]");
MaZiFAU
committed
call_method = recipe[0].m;
MaZiFAU
committed
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
);
MaZiFAU
committed
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
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
}
MaZiFAU
committed
Type[] from_args = from_me.GetGenericArguments();
Type partial_type = generic
.GetGenericTypeDefinition()
.MakeGenericType(!IsDirty
? from_args
: from_args[..(gene_sig - 1)]
MaZiFAU
committed
.Append(FuncExtensions.CreateFuncType(from_args[(gene_sig - 1)..]))
.ToArray()
);
MaZiFAU
committed
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
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 =
MaZiFAU
committed
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
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);
}
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
)
: Expression.Lambda(
Expression.Property(lambda_applicant[0].Body, property_name,
lambda_applicant.Skip(1).Select(l => l.Body).ToArray()),
bound_params
);
/// <summary>
/// Casts lambda_arguments into <c>int</c>s and uses them to (multidimensional) index lambda_applicant.<paramref name="property_name"/>
/// </summary>
/// <param name="property_name">name of property to index</param>
=> (LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params)
=> Expression.Lambda(
Expression.Property(lambda_applicant[0].Body, property_name,
lambda_applicant.Skip(1).Select(l => Expression.Convert(l.Body, typeof(int))).ToArray()),
bound_params
#pragma warning restore IDE0060 // Nicht verwendete Parameter entfernen // Signatures given by CustomFunction