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(), func.Value.MethodInfoFunction.Method.GetMethods()[0].Attribute.OverrideReturnType, false) { 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)), true)); UserDataTypeHandler.LoadType(); funcs.Add("list", new ListLibrary().ToScriptType()); boundFuncs.Add("list", new UserDataVariableSymbol("list", BoundTypeHandler.GetTypeDefinition(typeof(ListLibrary)), true)); UserDataTypeHandler.LoadType(); funcs.Add("string", new StringLibrary().ToScriptType()); boundFuncs.Add("string", new UserDataVariableSymbol("string", BoundTypeHandler.GetTypeDefinition(typeof(StringLibrary)), true)); var scope = new EvaluationScope(funcs); var boundScope = new BoundScope(boundFuncs, null); return (scope, boundScope); } public static void RegisterStaticVariable(string name, object value) { if (value is ScriptType scriptType) { var symbol = new VariableSymbol(name, scriptType.Type, true); BoundScope.AssignToNearest(symbol); Scope.AssignToNearest(symbol, scriptType); return; } var luaVariable = value.ToScriptType(); var ubDef = BoundTypeHandler.GetTypeDefinition(value.GetType()); VariableSymbol varSymbol = null; if (ubDef != null) { varSymbol = new UserDataVariableSymbol(name, ubDef, true); } 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, Type.Nil, parameters.ToArray(), null, false); } 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(), null, false); } public static TypeContainer DeriveValidTypes(System.Type type) { if (type.IsEnum) { var boundEnumType = BoundTypeHandler.GetTypeName(type); if (boundEnumType != null) return new TypeContainer(Type.UserData | Type.Number, boundEnumType); return new UndefinedUserDataTypeContainer(Type.UserData | Type.Number, type); } 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; } 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(object)) return Type.Unknown; if (type == typeof(ScriptType)) // allows every type return Type.Unknown; return new UndefinedUserDataTypeContainer(Type.UserData, type); } } }