using System; using System.Collections; using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; using Upsilon.BaseTypes; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTable; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BaseTypes.UserData; using Upsilon.Binder; using Upsilon.Binder.VariableSymbols; using Upsilon.BoundTypes; using Upsilon.Evaluator; using Type = Upsilon.BaseTypes.Type; namespace Upsilon.StandardLibraries { public static class StaticScope { private static EvaluationScope _staticScope; private static BoundScope _staticBoundScope; public static EvaluationScope Scope { get { if (_staticScope != null) return _staticScope; var (evaluationScope, boundScope) = CreateStandardLibrary(); _staticScope = evaluationScope; _staticBoundScope = boundScope; return _staticScope; } } public static BoundScope BoundScope { get { if (_staticBoundScope != null) return _staticBoundScope; var (evaluationScope, boundScope) = CreateStandardLibrary(); _staticScope = evaluationScope; _staticBoundScope = boundScope; return _staticBoundScope; } set => _staticBoundScope = value; } public static (EvaluationScope, BoundScope) CreateStandardLibrary() { var basicFunctions = new BasicFunctions().LoadMethods(); var funcs = new Dictionary(); var boundFuncs = new Dictionary(); foreach (var func in basicFunctions) { funcs.Add(func.Key, func.Value.MethodInfoFunction); var functionSymbol = new InternalFunctionVariableSymbol(func.Key, true, func.Value.MethodInfoFunction.ReturnType.GetScriptType(), func.Value.MethodInfoFunction.GetParameterInfo().Select(typeInfo => { var derivedType = DeriveValidTypes(typeInfo.Type); return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType, typeInfo.IsOptional); }).ToArray()) { CommentValue = func.Value.CommentValue?.Split('\n') }; boundFuncs.Add(func.Key, functionSymbol); } UserDataTypeHandler.LoadType(); funcs.Add("math", new MathLibrary().ToScriptType()); boundFuncs.Add("math", new UserDataVariableSymbol("math", BoundTypeHandler.GetTypeDefinition(typeof(MathLibrary)))); var scope = new EvaluationScope(funcs); var boundScope = new BoundScope(boundFuncs, null); return (scope, boundScope); } public static void RegisterStaticVariable(string name, object value) { var luaVariable = value.ToScriptType(); var ubDef = BoundTypeHandler.GetTypeDefinition(value.GetType()); VariableSymbol varSymbol = null; if (ubDef != null) { varSymbol = new UserDataVariableSymbol(name, ubDef); } else { var type = value.GetType(); if (type.IsGenericType) { var generic = type.GetGenericTypeDefinition(); if (generic.FullName != null && generic.FullName.StartsWith("System.Func")) { varSymbol = BuildFunctionVariableSymbol(name, type); } else if (generic.FullName != null && generic.FullName.StartsWith("System.Action")) { varSymbol = BuildActionVariableSymbol(name, type); } } if (varSymbol == null) throw new Exception("Unknown type: " + type); } BoundScope.AssignToNearest(varSymbol); Scope.AssignToNearest(varSymbol, luaVariable); } private static VariableSymbol BuildFunctionVariableSymbol(string name, System.Type type) { var genericParameters = type.GetGenericArguments(); var parameters = new List(); if (genericParameters.Length > 1) { for (var i = 0; i < genericParameters.Length - 1; i++) { var t = DeriveValidTypes(genericParameters[i]); parameters.Add(new InternalFunctionVariableSymbol.InternalFunctionParameter(name, t, false)); } } var result = genericParameters[genericParameters.Length - 1].GetScriptType(); return new InternalFunctionVariableSymbol(name, true, result, parameters.ToArray()); } private static VariableSymbol BuildActionVariableSymbol(string name, System.Type type) { var genericParameters = type.GetGenericArguments(); return new InternalFunctionVariableSymbol(name, true, Type.Nil, genericParameters.Select(DeriveValidTypes).Select(t => new InternalFunctionVariableSymbol.InternalFunctionParameter(name, t, false)).ToArray()); } public static Type DeriveValidTypes(System.Type type) { if (type.IsEnum) return Type.UserData | Type.Number; var typeCode = System.Type.GetTypeCode(type); switch (typeCode) { case TypeCode.Boolean: return Type.Boolean; case TypeCode.Byte: case TypeCode.SByte: case TypeCode.Decimal: case TypeCode.Double: case TypeCode.Single: case TypeCode.Int16: case TypeCode.Int32: case TypeCode.Int64: case TypeCode.UInt16: case TypeCode.UInt32: case TypeCode.UInt64: return Type.Number; case TypeCode.Char: case TypeCode.String: return Type.String; case TypeCode.Object: // allows every type return (Type) 255; } if (type == typeof(ScriptString)) return Type.String; if (typeof(ScriptNumber).IsAssignableFrom(type)) return Type.Number; if (type == typeof(ScriptBoolean)) return Type.Boolean; if (typeof(ScriptTable).IsAssignableFrom(type)) return Type.Table; if (type == typeof(IIterable)) return Type.Table | Type.UserData; if (typeof(IEnumerable).IsAssignableFrom(type)) return Type.Table | Type.UserData; if (type == typeof(ScriptType)) // allows every type return (Type) 255; return Type.UserData; } } }