diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs index 041f7ef..3787024 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs @@ -44,16 +44,6 @@ namespace Upsilon.BaseTypes.ScriptFunction public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope) { - var types = _directTypeManipulation - ? variables.Select(x => x.GetType()).ToArray() - : variables.Select(x => x.GetCSharpType()).ToArray(); - var method = _method.GetMethod(types); - if (method == null) - { - throw new Exception( - $"No valid function found on type '{_object.GetType()}' with name '{_method.Name}' " + - $"and parameter types: {string.Join(", ", types.Select(x => $"'{x.Name}'"))}"); - } var objects = new List(); if (_passScriptReference) objects.Add(script); @@ -62,19 +52,11 @@ namespace Upsilon.BaseTypes.ScriptFunction objects.AddRange(_directTypeManipulation ? variables.Select(x => (object) x).ToList() : variables.Select(x => x.ToCSharpObject()).ToList()); - var pars = method.GetParameters(); - if (pars.Length != objects.Count) - { - for (var i = objects.Count; i < pars.Length; i++) - { - objects.Add(null); - } - } - object result; try { - result = method.Invoke(_object, objects.ToArray()); + result = _object.GetType().InvokeMember(_method.Name, BindingFlags.InvokeMethod, System.Type.DefaultBinder, + _object, objects.ToArray()); } catch (TargetInvocationException e) { diff --git a/Upsilon/BaseTypes/TypeConversion.cs b/Upsilon/BaseTypes/TypeConversion.cs index 8a5c738..8d4fc82 100644 --- a/Upsilon/BaseTypes/TypeConversion.cs +++ b/Upsilon/BaseTypes/TypeConversion.cs @@ -5,6 +5,7 @@ using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptFunction; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BaseTypes.UserData; +using Upsilon.Text; namespace Upsilon.BaseTypes { @@ -19,6 +20,8 @@ namespace Upsilon.BaseTypes return new ScriptBoolean(b); case int i: return new ScriptNumberLong(i); + case short s: + return new ScriptNumberLong(s); case long i: return new ScriptNumberLong(i); case float f: diff --git a/Upsilon/BaseTypes/UserData/GenericUserData.cs b/Upsilon/BaseTypes/UserData/GenericUserData.cs index bda6b39..ce943d3 100644 --- a/Upsilon/BaseTypes/UserData/GenericUserData.cs +++ b/Upsilon/BaseTypes/UserData/GenericUserData.cs @@ -5,10 +5,10 @@ namespace Upsilon.BaseTypes.UserData { internal sealed class GenericUserData : ScriptType, IUserData { - public GenericUserData(object dictionary) + public GenericUserData(object obj) { - Value = dictionary; - _typeInfo = UserDataTypeHandler.GetTypeInfo(dictionary.GetType()); + Value = obj; + _typeInfo = UserDataTypeHandler.GetTypeInfo(obj.GetType()); } public override Type Type => Type.UserData; diff --git a/Upsilon/BaseTypes/UserData/UpsilonHiddenAttribute.cs b/Upsilon/BaseTypes/UserData/UpsilonHiddenAttribute.cs new file mode 100644 index 0000000..836c90e --- /dev/null +++ b/Upsilon/BaseTypes/UserData/UpsilonHiddenAttribute.cs @@ -0,0 +1,9 @@ +using System; + +namespace Upsilon.BaseTypes.UserData +{ + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property | AttributeTargets.Method)] + public class UpsilonHiddenAttribute : Attribute + { + } +} \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UpsilonUserDataAttribute.cs b/Upsilon/BaseTypes/UserData/UpsilonUserDataAttribute.cs index 24a371c..823fb96 100644 --- a/Upsilon/BaseTypes/UserData/UpsilonUserDataAttribute.cs +++ b/Upsilon/BaseTypes/UserData/UpsilonUserDataAttribute.cs @@ -2,7 +2,8 @@ using System; namespace Upsilon.BaseTypes.UserData { - [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum, AllowMultiple = false, Inherited = true)] + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Interface, + AllowMultiple = false, Inherited = true)] public class UpsilonUserDataAttribute : Attribute { public UpsilonUserDataAttribute(string name) diff --git a/Upsilon/BaseTypes/UserData/UserDataMethod.cs b/Upsilon/BaseTypes/UserData/UserDataMethod.cs index a23dbd1..11649e7 100644 --- a/Upsilon/BaseTypes/UserData/UserDataMethod.cs +++ b/Upsilon/BaseTypes/UserData/UserDataMethod.cs @@ -38,6 +38,8 @@ namespace Upsilon.BaseTypes.UserData public UserDataMethodParameter(ParameterInfo info) { Type = info.ParameterType; + if (Type.IsGenericType) + Type = Type.GetGenericTypeDefinition(); IsOptional = info.IsOptional; } @@ -89,6 +91,7 @@ namespace Upsilon.BaseTypes.UserData break; } var parameter = parameterTypes[index]; + if (parameter.IsGenericType) parameter = parameter.GetGenericTypeDefinition(); if (userDataMethodParameter.Type != parameter && !userDataMethodParameter.Type.IsAssignableFrom(parameter)) { diff --git a/Upsilon/BaseTypes/UserData/UserDataType.cs b/Upsilon/BaseTypes/UserData/UserDataType.cs index 4e010bd..f1d0b67 100644 --- a/Upsilon/BaseTypes/UserData/UserDataType.cs +++ b/Upsilon/BaseTypes/UserData/UserDataType.cs @@ -10,12 +10,20 @@ namespace Upsilon.BaseTypes.UserData { public UserDataType(System.Type type) { + if (type.IsGenericType) + { + type = type.GetGenericTypeDefinition(); + } Type = type; Variables = type.GetFields().ToDictionary(x => x.Name.ToLowerInvariant(), x => x); Properties = type.GetProperties().ToDictionary(x => x.Name.ToLowerInvariant(), x => x); Methods = new Dictionary(); foreach (var methodInfo in type.GetMethods()) { + var hiddenAttribute = methodInfo.GetCustomAttribute(typeof(UpsilonHiddenAttribute)); + if (hiddenAttribute != null) + continue; + var commonName = methodInfo.Name.ToLowerInvariant(); var attribute = methodInfo.GetCustomAttribute(typeof(ScriptFunctionAttribute)); if (attribute is ScriptFunctionAttribute sfa ) @@ -43,7 +51,10 @@ namespace Upsilon.BaseTypes.UserData public (ScriptType Type, bool Failed, string Error) Get(object value, string member) { - if (value.GetType() != Type) + var valueType = value.GetType(); + if (valueType.IsGenericType) + valueType = valueType.GetGenericTypeDefinition(); + if (valueType != Type) return (null, true, "Invalid Type"); member = member.ToLowerInvariant(); if (Variables.TryGetValue(member, out var info)) @@ -93,6 +104,12 @@ namespace Upsilon.BaseTypes.UserData return (new ScriptNull(), true); } + // HACK: Ugly solution for generic methods + if (method.ContainsGenericParameters) + { + method = value.GetType().GetMethod(method.Name, new[] {par1.GetCSharpType(), par2.GetCSharpType()}); + } + return (method.Invoke(value, new[] {par1.ToCSharpObject(), par2.ToCSharpObject()}).ToScriptType(), false); } public (ScriptType Type, bool Failed) UnaryOperator(object value, ScriptType par1, OperatorType op) @@ -103,6 +120,12 @@ namespace Upsilon.BaseTypes.UserData return (new ScriptNull(), true); } + // HACK: Ugly solution for generic methods + if (method.ContainsGenericParameters) + { + method = value.GetType().GetMethod(method.Name, new[] {par1.GetCSharpType()}); + } + return (method.Invoke(value, new[] {par1.ToCSharpObject()}).ToScriptType(), false); } } diff --git a/Upsilon/BaseTypes/UserData/UserDataTypeHandler.cs b/Upsilon/BaseTypes/UserData/UserDataTypeHandler.cs index ce7f261..614f397 100644 --- a/Upsilon/BaseTypes/UserData/UserDataTypeHandler.cs +++ b/Upsilon/BaseTypes/UserData/UserDataTypeHandler.cs @@ -1,19 +1,23 @@ using System; +using System.Collections.Concurrent; using System.Collections.Generic; using System.Reflection; using Upsilon.BoundTypes; +using Upsilon.Exceptions; using Upsilon.StandardLibraries; +using Upsilon.Text; namespace Upsilon.BaseTypes.UserData { public static class UserDataTypeHandler { - private static readonly Dictionary Types = new Dictionary(); + private static readonly ConcurrentDictionary Types + = new ConcurrentDictionary(); public static void LoadType(System.Type t, string name) { var info = new UserDataType(t); - Types.Add(t, info); + Types.AddOrUpdate(t, info, (type, dataType) => dataType); UserDataBoundTypeDefinition boundType; if (t.IsEnum) { @@ -55,7 +59,14 @@ namespace Upsilon.BaseTypes.UserData internal static UserDataType GetTypeInfo(System.Type t) { - return Types[t]; + if (t.IsGenericType) + t = t.GetGenericTypeDefinition(); + if (Types.TryGetValue(t, out var result)) + return result; + else + { + throw new Exception($"Can't use type '{t.FullName}' as script type, it's not registered for use."); + } } } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs b/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs index 26b5251..71945a6 100644 --- a/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs +++ b/Upsilon/BaseTypes/UserData/UserDataTypeOperators.cs @@ -86,6 +86,9 @@ namespace Upsilon.BaseTypes.UserData { return null; } + if (t1.IsGenericType) t1 = t1.GetGenericTypeDefinition(); + if (t2.IsGenericType) t2 = t2.GetGenericTypeDefinition(); + foreach (var operatorMethod in m) { diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index e828ee8..d292a96 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -316,10 +316,12 @@ namespace Upsilon.Binder (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) indexerVariable).BoundTypeDefinition; var bDefProperty = parent.Properties[fullStopIndexExpression.Index.ToLowerInvariant()]; var boundDef = BoundTypeHandler.GetTypeDefinition(bDefProperty.ActualType); - return new UserDataVariableSymbol(fullStopIndexExpression.Index, boundDef, parent); + if (boundDef != null) + { + return new UserDataVariableSymbol(fullStopIndexExpression.Index, boundDef, parent); + } } - - if (indexerVariable.Type == Type.Unknown) + else if (indexerVariable.Type == Type.Unknown) { if (indexerVariable is UserDataVariableSymbol funcSymbol) { @@ -335,8 +337,7 @@ namespace Upsilon.Binder return new VariableSymbol(fullStopIndexExpression.Index, Type.Unknown, true); } } - - if (expression.Kind == BoundKind.BoundFunctionCallExpression) + else if (expression.Kind == BoundKind.BoundFunctionCallExpression) { return new VariableSymbol("", expression.Type, true); } diff --git a/Upsilon/BoundTypes/BoundTypeHandler.cs b/Upsilon/BoundTypes/BoundTypeHandler.cs index 3466e6c..2103008 100644 --- a/Upsilon/BoundTypes/BoundTypeHandler.cs +++ b/Upsilon/BoundTypes/BoundTypeHandler.cs @@ -1,5 +1,5 @@ using System.Collections; -using System.Collections.Generic; +using System.Collections.Concurrent; using System.Linq; using Upsilon.BaseTypes; @@ -7,33 +7,29 @@ namespace Upsilon.BoundTypes { public static class BoundTypeHandler { - private static readonly Dictionary DefaultTypeDefinitions = - new Dictionary + private static void AddDefaults(ConcurrentDictionary dic) { - {"string", new BoundTypeDefinition(Type.String, typeof(string))}, - { - "number", - new BoundTypeDefinition(Type.Number, - new[] {typeof(int), typeof(long), typeof(float), typeof(double)}) - }, - {"bool", new BoundTypeDefinition(Type.Boolean, typeof(bool))}, - {"table", new BoundTypeDefinition(Type.Table, typeof(IEnumerator))}, - {"function", new BoundTypeDefinition(Type.Function, new System.Type[0])}, - }; + dic.TryAdd("string", new BoundTypeDefinition(Type.String, typeof(string))); + dic.TryAdd("number", new BoundTypeDefinition(Type.Number, + new[] {typeof(int), typeof(long), typeof(float), typeof(double)})); + dic.TryAdd("bool", new BoundTypeDefinition(Type.Boolean, typeof(bool))); + dic.TryAdd("table", new BoundTypeDefinition(Type.Table, typeof(IEnumerator))); + dic.TryAdd("function", new BoundTypeDefinition(Type.Function, new System.Type[0])); + } - private static Dictionary _typeDefinitions = - new Dictionary(DefaultTypeDefinitions); + private static readonly ConcurrentDictionary TypeDefinitions = Reset(); - public static void Reset() + public static ConcurrentDictionary Reset() { - _typeDefinitions = - new Dictionary(DefaultTypeDefinitions); + var dic = new ConcurrentDictionary(); + AddDefaults(dic); + return dic; } public static BoundTypeDefinition GetTypeDefinition(string key) { var normalizedName = key.ToLowerInvariant(); - if (_typeDefinitions.TryGetValue(normalizedName, out var bt)) + if (TypeDefinitions.TryGetValue(normalizedName, out var bt)) { return bt; } @@ -42,20 +38,13 @@ namespace Upsilon.BoundTypes public static BoundTypeDefinition GetTypeDefinition(System.Type type) { - return _typeDefinitions.Values.FirstOrDefault(x => x.ValidInternalTypes.Contains(type)); + return TypeDefinitions.Values.FirstOrDefault(x => x.ValidInternalTypes.Contains(type)); } public static void LoadUserDataTypeDefinition(UserDataBoundTypeDefinition def) { var key = def.Name.ToLowerInvariant(); - if (_typeDefinitions.ContainsKey(key)) - { - _typeDefinitions[key] = def; - } - else - { - _typeDefinitions.Add(key, def); - } + TypeDefinitions.AddOrUpdate(key, def, (s, definition) => definition); } } } \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 16a1229..fc2f37a 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -75,9 +75,26 @@ namespace Upsilon.Evaluator { for (var index = 0; index < parameters.Length; index++) { - var parameter = parameters[index]; - var parameterSymbol = (UserDataVariableSymbol)function.Parameters[index].VariableSymbol; - if (parameterSymbol.BoundTypeDefinition != null) + object parameter; + if (index < parameters.Length) + { + parameter = parameters[index]; + } + else + { + parameter = null; + } + + UserDataVariableSymbol parameterSymbol; + if (index < function.Parameters.Length) + { + parameterSymbol = (UserDataVariableSymbol)function.Parameters[index].VariableSymbol; + } + else + { + continue; + } + if (parameterSymbol.BoundTypeDefinition != null && parameter != null) { bool isCompatible = false; var parameterType = parameter.GetType(); @@ -97,7 +114,8 @@ namespace Upsilon.Evaluator } } - innerEvaluator.Scope.CreateLocal(parameterSymbol, parameter.ToScriptType()); + var parameterConverted = parameter == null ? new ScriptNull() : parameter.ToScriptType(); + innerEvaluator.Scope.CreateLocal(parameterSymbol, parameterConverted); } } @@ -380,7 +398,7 @@ namespace Upsilon.Evaluator case BoundBinaryOperator.OperatorKind.Or: return new ScriptBoolean((ScriptBoolean) left || (ScriptBoolean) right); default: - throw new Exception("Invalid Binary Operator: " + e.Operator); + throw new Exception("Invalid Binary Operator: " + e.Operator.Kind); } } diff --git a/Upsilon/Evaluator/Script.cs b/Upsilon/Evaluator/Script.cs index f949ed8..f117be2 100644 --- a/Upsilon/Evaluator/Script.cs +++ b/Upsilon/Evaluator/Script.cs @@ -24,18 +24,32 @@ namespace Upsilon.Evaluator internal Script(string scriptString, string fileName, ScriptOptions options ) { - FileName = fileName; - Options = options; + FileName = fileName; + Options = options; _scriptString = scriptString; - ScriptString = new SourceText(scriptString); - Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError, fileName); + ScriptString = new SourceText(scriptString); + Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError, fileName); var staticBoundScope = options.OverrideStaticBoundScope ?? StaticScope.BoundScope; - var boundScope = BoundScope.WithReadOnlyScope(staticBoundScope); + var boundScope = BoundScope.WithReadOnlyScope(staticBoundScope); Binder = Upsilon.Binder.Binder.CreateWithSetScope(Diagnostics, boundScope, this); var staticScope = options.OverrideStaticScope ?? StaticScope.Scope; - Scope = EvaluationScope.CreateWithGetOnlyParent(staticScope); + Scope = EvaluationScope.CreateWithGetOnlyParent(staticScope); + Evaluator = Evaluator.CreateWithSetScope(Diagnostics, Scope, this); + } + + public Script(BoundScript s, ScriptOptions options ) + { + FileName = s.FileName; + Options = options; + Diagnostics = new Diagnostics(ScriptString, options.ThrowExceptionOnError, s.FileName); + + _bound = s; + Binder = Upsilon.Binder.Binder.CreateWithSetScope(Diagnostics, s.Scope, this); + + var staticScope = options.OverrideStaticScope ?? StaticScope.Scope; + Scope = EvaluationScope.CreateWithGetOnlyParent(staticScope); Evaluator = Evaluator.CreateWithSetScope(Diagnostics, Scope, this); } @@ -75,6 +89,7 @@ namespace Upsilon.Evaluator return s; } + internal void Parse() { _parsed = Parser.Parser.Parse(_scriptString, Diagnostics, Options.SaveDataComments); @@ -98,6 +113,8 @@ namespace Upsilon.Evaluator private static T Convert(ScriptType t) { + if (t == null) + return default(T); if (typeof(T) == typeof(double)) return (T)(object)System.Convert.ToDouble(t.ToCSharpObject()); if (typeof(T) == typeof(long)) diff --git a/Upsilon/Evaluator/ScriptLoader.cs b/Upsilon/Evaluator/ScriptLoader.cs index 2c8c075..9cfb2ee 100644 --- a/Upsilon/Evaluator/ScriptLoader.cs +++ b/Upsilon/Evaluator/ScriptLoader.cs @@ -4,12 +4,11 @@ namespace Upsilon.Evaluator { public class ScriptLoader { - public virtual string FilePath { get; } = "./"; - public virtual string ModulesPath { get; } = "./modules/"; + public virtual string FilePath { get; set; } = "./"; + public virtual string ModulesPath { get; set; } = "./modules/"; public virtual string LoadFile(string fileName) { - var path = FilePath; var resolvedName = ResolveFileName(FilePath, fileName); if (File.Exists(resolvedName)) { diff --git a/Upsilon/Executor.cs b/Upsilon/Executor.cs index ad35cff..a361573 100644 --- a/Upsilon/Executor.cs +++ b/Upsilon/Executor.cs @@ -19,6 +19,8 @@ namespace Upsilon { if (options == null) options = DefaultOptions; var input = options.ScriptLoader.LoadFile(fileName); + if (input == null) + return null; var script = new Script(input,fileName, options); script.Parse(); return script; @@ -70,6 +72,12 @@ namespace Upsilon return script.Evaluate(); } + public static object EvaluateScript(BoundScript input, ScriptOptions options = null) + { + var script = new Script(input, options); + return script.Evaluate(); + } + public static T EvaluateFunction(string input, string function, object[] parameters = null, ScriptOptions options = null) { var script = ParseInputAndEvaluate(input, options);