diff --git a/Upsilon/BaseTypes/Number/ScriptNumber.cs b/Upsilon/BaseTypes/Number/ScriptNumber.cs index 7d8924a..6a6400a 100644 --- a/Upsilon/BaseTypes/Number/ScriptNumber.cs +++ b/Upsilon/BaseTypes/Number/ScriptNumber.cs @@ -2,9 +2,9 @@ using System; namespace Upsilon.BaseTypes.Number { - internal abstract class ScriptNumber : ScriptType + public abstract class ScriptNumber : ScriptType { - protected abstract bool IsFloat { get; } + protected internal abstract bool IsFloat { get; } public override Type Type => Type.Number; diff --git a/Upsilon/BaseTypes/Number/ScriptNumberDouble.cs b/Upsilon/BaseTypes/Number/ScriptNumberDouble.cs index f64eac9..693230b 100644 --- a/Upsilon/BaseTypes/Number/ScriptNumberDouble.cs +++ b/Upsilon/BaseTypes/Number/ScriptNumberDouble.cs @@ -5,7 +5,7 @@ namespace Upsilon.BaseTypes.Number internal class ScriptNumberDouble : ScriptNumber { public double Value { get; } - protected override bool IsFloat { get; } = true; + protected internal override bool IsFloat { get; } = true; public ScriptNumberDouble(double value) { diff --git a/Upsilon/BaseTypes/Number/ScriptNumberLong.cs b/Upsilon/BaseTypes/Number/ScriptNumberLong.cs index ca766ed..b6bd156 100644 --- a/Upsilon/BaseTypes/Number/ScriptNumberLong.cs +++ b/Upsilon/BaseTypes/Number/ScriptNumberLong.cs @@ -6,7 +6,7 @@ namespace Upsilon.BaseTypes.Number internal class ScriptNumberLong : ScriptNumber { public long Value { get; set; } - protected override bool IsFloat { get; } = false; + protected internal override bool IsFloat { get; } = false; public ScriptNumberLong(long val) { diff --git a/Upsilon/BaseTypes/ScriptBoolean.cs b/Upsilon/BaseTypes/ScriptBoolean.cs index 72731df..ecce633 100644 --- a/Upsilon/BaseTypes/ScriptBoolean.cs +++ b/Upsilon/BaseTypes/ScriptBoolean.cs @@ -1,6 +1,6 @@ namespace Upsilon.BaseTypes { - internal class ScriptBoolean : ScriptType + public class ScriptBoolean : ScriptType { public ScriptBoolean(bool value) { diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs index dc8039a..041f7ef 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs @@ -19,6 +19,15 @@ namespace Upsilon.BaseTypes.ScriptFunction _passScopeReference = passScopeReference; ReturnType = _method.ReturnType; + + if (method.GetMethods().First().Attribute != null) + { + var attr = method.GetMethods().First().Attribute; + _directTypeManipulation = attr.DirectScriptManipulation; + _passScriptReference = attr.PassScriptReference; + _passScopeReference = attr.PassScopeReference; + + } } private readonly UserDataMethod _method; diff --git a/Upsilon/BaseTypes/UserData/GenericUserData.cs b/Upsilon/BaseTypes/UserData/GenericUserData.cs index 3930fb9..bda6b39 100644 --- a/Upsilon/BaseTypes/UserData/GenericUserData.cs +++ b/Upsilon/BaseTypes/UserData/GenericUserData.cs @@ -3,7 +3,7 @@ using Upsilon.Text; namespace Upsilon.BaseTypes.UserData { - internal class GenericUserData : ScriptType, IUserData + internal sealed class GenericUserData : ScriptType, IUserData { public GenericUserData(object dictionary) { @@ -12,7 +12,7 @@ namespace Upsilon.BaseTypes.UserData } public override Type Type => Type.UserData; - protected virtual object Value { get; } + private object Value { get; } private readonly UserDataType _typeInfo; public override object ToCSharpObject() @@ -25,7 +25,7 @@ namespace Upsilon.BaseTypes.UserData return Value.GetType(); } - public virtual ScriptType Get(Diagnostics diagnostics, TextSpan span, ScriptType index, EvaluationScope scope) + public ScriptType Get(Diagnostics diagnostics, TextSpan span, ScriptType index, EvaluationScope scope) { var s = index.ToCSharpObject().ToString(); var (type, failed, error) = _typeInfo.Get(Value, s); @@ -36,7 +36,7 @@ namespace Upsilon.BaseTypes.UserData return type; } - public virtual void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value) + public void Set(Diagnostics diagnostics, TextSpan span, ScriptType index, ScriptType value) { var s = index.ToCSharpObject().ToString(); var (failed, error) = _typeInfo.Set(Value, s, value); diff --git a/Upsilon/BaseTypes/UserData/UserDataMethod.cs b/Upsilon/BaseTypes/UserData/UserDataMethod.cs index fcb864a..30443dd 100644 --- a/Upsilon/BaseTypes/UserData/UserDataMethod.cs +++ b/Upsilon/BaseTypes/UserData/UserDataMethod.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Upsilon.Evaluator; +using Upsilon.StandardLibraries; namespace Upsilon.BaseTypes.UserData { @@ -13,6 +14,8 @@ namespace Upsilon.BaseTypes.UserData { Method = method; var pars = method.GetParameters(); + Attribute = (ScriptFunctionAttribute) method.GetCustomAttribute(typeof(ScriptFunctionAttribute)); + var ls = new List(); foreach (var parameter in pars) { @@ -26,6 +29,7 @@ namespace Upsilon.BaseTypes.UserData } public MethodInfo Method { get; } + public ScriptFunctionAttribute Attribute { get; } public UserDataMethodParameter[] Parameters { get; } } diff --git a/Upsilon/BaseTypes/UserData/UserDataType.cs b/Upsilon/BaseTypes/UserData/UserDataType.cs index 0ff22e7..4e010bd 100644 --- a/Upsilon/BaseTypes/UserData/UserDataType.cs +++ b/Upsilon/BaseTypes/UserData/UserDataType.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Linq; using System.Reflection; using Upsilon.BaseTypes.ScriptFunction; +using Upsilon.StandardLibraries; namespace Upsilon.BaseTypes.UserData { @@ -16,6 +17,12 @@ namespace Upsilon.BaseTypes.UserData foreach (var methodInfo in type.GetMethods()) { var commonName = methodInfo.Name.ToLowerInvariant(); + var attribute = methodInfo.GetCustomAttribute(typeof(ScriptFunctionAttribute)); + if (attribute is ScriptFunctionAttribute sfa ) + { + commonName = sfa.Name.ToLowerInvariant(); + } + if (Methods.TryGetValue(commonName, out var methodData)) { methodData.LoadMethodPart(methodInfo); diff --git a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs index 4d25de9..2319b36 100644 --- a/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs +++ b/Upsilon/BoundTypes/UserDataBoundTypeDefinition.cs @@ -1,7 +1,9 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Upsilon.BaseTypes; +using Upsilon.StandardLibraries; using Type = Upsilon.BaseTypes.Type; namespace Upsilon.BoundTypes @@ -52,12 +54,25 @@ namespace Upsilon.BoundTypes { obj.Properties.Add(f.Name.ToLowerInvariant(), f); } - var methods = backingType.GetMethods().Select(x => new UserDataBoundMethod() + var methods = new List(); + var backingMethods = backingType.GetMethods(); + foreach (var backingMethod in backingMethods) { - Name = x.Name, - Type = Type.Function, - ResultType = x.ReturnType.GetScriptType() - }); + if (backingMethod.IsSpecialName) + continue; + var name = backingMethod.Name; + var attribute = backingMethod.GetCustomAttribute(typeof(ScriptFunctionAttribute)); + if (attribute is ScriptFunctionAttribute sfa ) + { + name = sfa.Name; + } + methods.Add(new UserDataBoundMethod() + { + Name = name, + Type = Type.Function, + ResultType = backingMethod.ReturnType.GetScriptType() + }); + } foreach (var f in methods) { obj.Properties.Add(f.Name.ToLowerInvariant(), f); diff --git a/Upsilon/StandardLibraries/BasicFunctions.cs b/Upsilon/StandardLibraries/BasicFunctions.cs index 9d189c2..d862eef 100644 --- a/Upsilon/StandardLibraries/BasicFunctions.cs +++ b/Upsilon/StandardLibraries/BasicFunctions.cs @@ -13,8 +13,9 @@ namespace Upsilon.StandardLibraries { internal class BasicFunctions : ScriptLibrary { - [StandardLibraryScriptFunction("assert", "Asserts that the parameter passed is true. Throws an exception if it is not true. " + - "Can take a message to show in the exception, otherwise throws with message \"assertion failed!\"")] + [ScriptFunction("assert", "Asserts that the parameter passed is true. Throws an exception if it is not true. " + + "Can take a message to show in the exception, otherwise throws with message \"assertion failed!\"", + directScriptManipulation: true)] public void Assert(ScriptBoolean boolean, ScriptString message = null) { if (!boolean) @@ -23,26 +24,29 @@ namespace Upsilon.StandardLibraries } } - [StandardLibraryScriptFunction("error", "Throw an exception with given error message")] + [ScriptFunction("error", "Throw an exception with given error message", + directScriptManipulation: true)] public void Error(ScriptString message) { throw new Exception(message.Value); } - [StandardLibraryScriptFunction("ipairs", "Iterates over an iterable variable, like a table, until it encounters a nil value.")] + [ScriptFunction("ipairs", "Iterates over an iterable variable, like a table, until it encounters a nil value.", + directScriptManipulation: true)] public IIterable UpTillNullPairs(IIterable table) { return new UpTillNullPairsScriptIterator(table); } - [StandardLibraryScriptFunction("pairs", "Iterates over an iterable variable, like a table, skipping all nil values.")] + [ScriptFunction("pairs", "Iterates over an iterable variable, like a table, skipping all nil values.", + directScriptManipulation: true)] public IIterable Pairs(IIterable table) { return new PairsScriptIterator(table); } - [StandardLibraryScriptFunction("require", "Loads a module from the module path, using the given script loader.", - true)] + [ScriptFunction("require", "Loads a module from the module path, using the given script loader.", + true, true)] public ScriptType Require(Script script, ScriptString fileName) { var file = fileName.Value; @@ -57,7 +61,7 @@ namespace Upsilon.StandardLibraries } - [StandardLibraryScriptFunction("tonumber", "Parses a string to a number.")] + [ScriptFunction("tonumber", "Parses a string to a number.", directScriptManipulation: true)] public ScriptNumber ToNumber(ScriptString obj) { var str = obj.Value; @@ -71,13 +75,13 @@ namespace Upsilon.StandardLibraries } } - [StandardLibraryScriptFunction("tostring", "Returns the string value of the given object.")] + [ScriptFunction("tostring", "Returns the string value of the given object.", directScriptManipulation: true)] public ScriptString ToString(ScriptType obj) { return new ScriptString(obj.ToString()); } - [StandardLibraryScriptFunction("type", "Returns the type of the given object as a string.")] + [ScriptFunction("type", "Returns the type of the given object as a string.", directScriptManipulation: true)] public ScriptString Type(ScriptType obj) { return new ScriptString(obj.Type.ToString()); diff --git a/Upsilon/StandardLibraries/MathLibrary.cs b/Upsilon/StandardLibraries/MathLibrary.cs new file mode 100644 index 0000000..0a1fb47 --- /dev/null +++ b/Upsilon/StandardLibraries/MathLibrary.cs @@ -0,0 +1,18 @@ +using System; +using Upsilon.BaseTypes.Number; + +namespace Upsilon.StandardLibraries +{ + internal class MathLibrary + { + [ScriptFunction("abs", "Returns the absolute value of x. (integer/float)", directScriptManipulation: true)] + public ScriptNumber Absolute(ScriptNumber a) + { + if (a.IsFloat) + { + return new ScriptNumberDouble(Math.Abs(((ScriptNumberDouble) a).Value)); + } + return new ScriptNumberLong(Math.Abs(((ScriptNumberLong) a).Value)); + } + } +} \ No newline at end of file diff --git a/Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs b/Upsilon/StandardLibraries/ScriptFunctionAttribute.cs similarity index 58% rename from Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs rename to Upsilon/StandardLibraries/ScriptFunctionAttribute.cs index a049259..2b5fe70 100644 --- a/Upsilon/StandardLibraries/StandardLibraryScriptFunctionAttribute.cs +++ b/Upsilon/StandardLibraries/ScriptFunctionAttribute.cs @@ -3,19 +3,21 @@ using System; namespace Upsilon.StandardLibraries { [AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = true)] - public class StandardLibraryScriptFunctionAttribute : Attribute + public class ScriptFunctionAttribute : Attribute { - public StandardLibraryScriptFunctionAttribute(string name, string comment = null, - bool passScriptReference = false, bool passScopeReference = false) + public ScriptFunctionAttribute(string name, string comment = null, + bool directScriptManipulation = false, bool passScriptReference = false, bool passScopeReference = false) { Name = name; Comment = comment; + DirectScriptManipulation = directScriptManipulation; PassScriptReference = passScriptReference; PassScopeReference = passScopeReference; } public string Name { get; } public string Comment { get; } + public bool DirectScriptManipulation { get; } public bool PassScriptReference { get; } public bool PassScopeReference { get; } } diff --git a/Upsilon/StandardLibraries/ScriptLibrary.cs b/Upsilon/StandardLibraries/ScriptLibrary.cs index 9104931..daec8cf 100644 --- a/Upsilon/StandardLibraries/ScriptLibrary.cs +++ b/Upsilon/StandardLibraries/ScriptLibrary.cs @@ -14,7 +14,7 @@ namespace Upsilon.StandardLibraries var methods = GetType().GetMethods(); foreach (var methodInfo in methods) { - var attr = methodInfo.GetCustomAttribute(); + var attr = methodInfo.GetCustomAttribute(); if (attr != null) { dictionary.Add(attr.Name, new LoadedStandardFunction() diff --git a/Upsilon/StandardLibraries/StaticScope.cs b/Upsilon/StandardLibraries/StaticScope.cs index b3bc540..239edd9 100644 --- a/Upsilon/StandardLibraries/StaticScope.cs +++ b/Upsilon/StandardLibraries/StaticScope.cs @@ -6,6 +6,7 @@ using System.Linq; using Upsilon.BaseTypes; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; +using Upsilon.BaseTypes.UserData; using Upsilon.Binder; using Upsilon.Binder.VariableSymbols; using Upsilon.BoundTypes; @@ -68,6 +69,12 @@ namespace Upsilon.StandardLibraries }; boundFuncs.Add(func.Key, functionSymbol); } + + UserDataTypeHandler.LoadType(); + funcs.Add("math", new MathLibrary().ToScriptType()); + boundFuncs.Add("math", + new FunctionParameterSymbol("math", BoundTypeHandler.GetTypeDefinition(typeof(MathLibrary)))); + var scope = new EvaluationScope(funcs); var boundScope = new BoundScope(boundFuncs, null); return (scope, boundScope); diff --git a/UpsilonTests/GeneralTests/BasicMathExpressions.cs b/UpsilonTests/GeneralTests/BasicMathExpressions.cs index 9eef6ad..3fd56ee 100644 --- a/UpsilonTests/GeneralTests/BasicMathExpressions.cs +++ b/UpsilonTests/GeneralTests/BasicMathExpressions.cs @@ -50,7 +50,7 @@ namespace UpsilonTests.GeneralTests [Theory] [InlineData("1/1", 1)] [InlineData("1000 / 10", 100)] - [InlineData("656486 / 5146", 127)] + [InlineData("656486 / 5146", 127.57209483)] [InlineData("656486 / 5146.0", 127.57209483)] public void Divison(string input, double expectedOutput) {