using System.Collections.Generic; using System.Linq.Expressions; using System; using System.Linq; using UnityEngine; 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 }, }; private static readonly Dictionary<string, ExpressionType> MMTtoBinaryExpressionType = new() { { MMTConstants.Add, 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}, { "LessThan", ExpressionType.LessThan}, { "LessThanOrEqual", ExpressionType.LessThanOrEqual}, { "Modulo", ExpressionType.Modulo}, { "ModuloAssign", ExpressionType.ModuloAssign}, { MMTConstants.Multiply, 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 var 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 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 ); return MakeObjArray(lambda_applicant, lambda_arguments, bound_params); } 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].Body, lambda_applicant[0].ReturnType.GetMethod("Insert"), 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)), null ); private static LambdaExpression Identity0(LambdaExpression[] lambda_applicant, LambdaExpression[] lambda_arguments, ParameterExpression[] bound_params) => lambda_applicant[0]; } } }