Upsilon/Upsilon/StandardLibraries/StaticScope.cs

215 lines
8.6 KiB
C#

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<string, ScriptType>();
var boundFuncs = new Dictionary<string, VariableSymbol>();
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<MathLibrary>();
funcs.Add("math", new MathLibrary().ToScriptType());
boundFuncs.Add("math",
new UserDataVariableSymbol("math", BoundTypeHandler.GetTypeDefinition(typeof(MathLibrary)), true));
UserDataTypeHandler.LoadType<ListLibrary>();
funcs.Add("list", new ListLibrary().ToScriptType());
boundFuncs.Add("list",
new UserDataVariableSymbol("list", BoundTypeHandler.GetTypeDefinition(typeof(ListLibrary)), true));
UserDataTypeHandler.LoadType<StringLibrary>();
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<InternalFunctionVariableSymbol.InternalFunctionParameter>();
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);
}
}
}