diff --git a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs index 49882bd..3e43b15 100644 --- a/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs +++ b/Upsilon/BaseTypes/ScriptFunction/ScriptMethodInfoFunction.cs @@ -24,6 +24,11 @@ namespace Upsilon.BaseTypes.ScriptFunction private readonly bool _passScriptReference; public System.Type ReturnType { get; } + public IEnumerable GetParameterTypes() + { + return _method.GetMethods().First().Parameters.Select(x => x.Type); + } + public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script) { var types = _directTypeManipulation diff --git a/Upsilon/BaseTypes/ScriptTable.cs b/Upsilon/BaseTypes/ScriptTable.cs index 2983493..79c37ac 100644 --- a/Upsilon/BaseTypes/ScriptTable.cs +++ b/Upsilon/BaseTypes/ScriptTable.cs @@ -4,6 +4,7 @@ using System.Linq; using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Binder; +using Upsilon.Binder.VariableSymbols; using Upsilon.Evaluator; using Upsilon.Text; diff --git a/Upsilon/BaseTypes/Type.cs b/Upsilon/BaseTypes/Type.cs index a6b9991..26e4b98 100644 --- a/Upsilon/BaseTypes/Type.cs +++ b/Upsilon/BaseTypes/Type.cs @@ -1,15 +1,18 @@ +using System; + namespace Upsilon.BaseTypes { - public enum Type + [Flags] + public enum Type : byte { - Unknown, - Nil, - Boolean, - Number, - String, - Function, - UserData, - Thread, - Table, + Unknown = 0, + Nil = 1, + Boolean = 2, + Number = 4, + String = 8, + Function = 16, + UserData = 32, + Thread = 64, + Table = 128, } } \ No newline at end of file diff --git a/Upsilon/BaseTypes/UserData/UserDataMethod.cs b/Upsilon/BaseTypes/UserData/UserDataMethod.cs index 928467c..2d93c02 100644 --- a/Upsilon/BaseTypes/UserData/UserDataMethod.cs +++ b/Upsilon/BaseTypes/UserData/UserDataMethod.cs @@ -6,7 +6,7 @@ namespace Upsilon.BaseTypes.UserData { public class UserDataMethod { - private class UserDataMethodPart + public class UserDataMethodPart { public UserDataMethodPart(MethodInfo method) { @@ -18,7 +18,7 @@ namespace Upsilon.BaseTypes.UserData public UserDataMethodParameter[] Parameters { get; } } - private struct UserDataMethodParameter + public struct UserDataMethodParameter { public UserDataMethodParameter(ParameterInfo info) { @@ -51,6 +51,11 @@ namespace Upsilon.BaseTypes.UserData MethodParts.Add(part); } + public List GetMethods() + { + return MethodParts; + } + public MethodInfo GetMethod(System.Type[] parameterTypes) { foreach (var userDataMethodPart in MethodParts) diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 81e8fdf..ae97fb1 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -4,6 +4,7 @@ using System.Collections.Immutable; using System.Linq; using Upsilon.BaseTypes; using Upsilon.BaseTypes.Number; +using Upsilon.Binder.VariableSymbols; using Upsilon.BoundTypes; using Upsilon.Parser; using Type = Upsilon.BaseTypes.Type; @@ -55,7 +56,7 @@ namespace Upsilon.Binder var resultType = Scope.ReturnType; Scope = Scope.ParentScope; var variable = - (FunctionVariableSymbol) unboundFunctionStatement.Value.Scope.ParentScope.Variables[ + (ScriptFunctionVariableSymbol) unboundFunctionStatement.Value.Scope.ParentScope.Variables[ unboundFunctionStatement.Key]; variable.IsBound = true; variable.ResultType = resultType; @@ -209,59 +210,39 @@ namespace Upsilon.Binder var returnType = Type.Unknown; if (ResolveVariable(expression) is FunctionVariableSymbol function) { - if (!function.IsBound) + if (function is ScriptFunctionVariableSymbol scriptFunction) { - Scope = new BoundScope(Scope); - for (var index = 0; index < function.Parameters.Length; index++) + if (!scriptFunction.IsBound) { - var functionVariable = function.Parameters[index]; - var callingVariable = parameters[index]; - functionVariable.Type = callingVariable.Type; - Scope.DefineLocalVariable(functionVariable); - } + Scope = new BoundScope(Scope); + for (var index = 0; index < scriptFunction.Parameters.Length; index++) + { + var functionVariable = scriptFunction.Parameters[index]; + var callingVariable = parameters[index]; + functionVariable.Type = callingVariable.Type; + Scope.DefineLocalVariable(functionVariable); + } - var unboundFunctionStatement = _unboundFunctions[function.Name]; - unboundFunctionStatement.Block = - (BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock); - returnType = Scope.ReturnType; - Scope = Scope.ParentScope; - function.IsBound = true; - function.ResultType = returnType; - _unboundFunctions.Remove(function.Name); + var unboundFunctionStatement = _unboundFunctions[scriptFunction.Name]; + unboundFunctionStatement.Block = + (BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock); + returnType = Scope.ReturnType; + Scope = Scope.ParentScope; + scriptFunction.IsBound = true; + scriptFunction.ResultType = returnType; + _unboundFunctions.Remove(scriptFunction.Name); + } } returnType = function.ResultType; - var pars = function.Parameters; - - - if (!function.IsInternal) + var (isValid, error, wrongParameter) = function.ValidateParameters(parameters.ToImmutable()); + if (!isValid) { - if (pars.Length != parameters.Count) - { - _diagnostics.LogError( - $"Invalid number of parameters for function '{function.Name}'. Expected {pars.Length}, got {parameters.Count}", - e.Span); - } - else - { - for (var i = 0; i < pars.Length; i++) - { - var functionParameter = pars[i]; - var callingParameter = parameters[i]; - if (functionParameter.Type != Type.Unknown && - callingParameter.Type != Type.Unknown && callingParameter.Type != Type.Nil) - { - if (callingParameter.Type != functionParameter.Type) - { - _diagnostics.LogError( - $"Invalid type for function '{function.Name}' at parameter '{functionParameter.Name}'. " + - $"Expected type '{functionParameter.Type}', got '{callingParameter.Type}'", - callingParameter.Span); - } - } - } - } + var span = e.Span; + if (wrongParameter != null) + span = wrongParameter.Span; + _diagnostics.LogError(error, span); } } return new BoundFunctionCallExpression(expression, parameters.ToImmutable(), e.Span, returnType); @@ -532,7 +513,7 @@ namespace Upsilon.Binder if (!Scope.TryGetVariable(name, !isLocal, out var variable)) { - var functionVariable = new FunctionVariableSymbol(name, isLocal, parameters.ToImmutable(), func.ReturnType, false) + var functionVariable = new ScriptFunctionVariableSymbol(name, isLocal, parameters.ToImmutable(), func.ReturnType) { CommentValue = commentData.ToArray() }; diff --git a/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs b/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs index 3e15137..47c2181 100644 --- a/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs +++ b/Upsilon/Binder/BoundExpressions/BoundTableExpression.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using Upsilon.BaseTypes; +using Upsilon.Binder.VariableSymbols; using Upsilon.Text; namespace Upsilon.Binder diff --git a/Upsilon/Binder/BoundScope.cs b/Upsilon/Binder/BoundScope.cs index 25eeefc..c62f67d 100644 --- a/Upsilon/Binder/BoundScope.cs +++ b/Upsilon/Binder/BoundScope.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using Upsilon.Binder.VariableSymbols; using Type = Upsilon.BaseTypes.Type; namespace Upsilon.Binder diff --git a/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs b/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs index 30a5f76..0104e74 100644 --- a/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundFunctionAssignmentStatement.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Upsilon.Binder.VariableSymbols; using Upsilon.Text; namespace Upsilon.Binder diff --git a/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs b/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs index 9844a6f..2bfd7c9 100644 --- a/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundGenericForStatement.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Upsilon.Binder.VariableSymbols; using Upsilon.Text; namespace Upsilon.Binder diff --git a/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs b/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs index 1d35623..af136a7 100644 --- a/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundMultiAssignmentStatement.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Collections.Immutable; +using Upsilon.Binder.VariableSymbols; using Upsilon.Text; namespace Upsilon.Binder diff --git a/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs b/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs index 3c9daca..cb25589 100644 --- a/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs +++ b/Upsilon/Binder/BoundStatements/BoundNumericForStatement.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using Upsilon.Binder.VariableSymbols; using Upsilon.Text; namespace Upsilon.Binder diff --git a/Upsilon/Binder/BoundVariableSymbol.cs b/Upsilon/Binder/BoundVariableSymbol.cs index 4aa3f61..32b0bde 100644 --- a/Upsilon/Binder/BoundVariableSymbol.cs +++ b/Upsilon/Binder/BoundVariableSymbol.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using Upsilon.BaseTypes; +using Upsilon.Binder.VariableSymbols; using Upsilon.Text; namespace Upsilon.Binder diff --git a/Upsilon/Binder/VariableSymbol.cs b/Upsilon/Binder/VariableSymbol.cs deleted file mode 100644 index 6356b42..0000000 --- a/Upsilon/Binder/VariableSymbol.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System.Collections.Generic; -using System.Collections.Immutable; -using Upsilon.BaseTypes; -using Upsilon.BoundTypes; - -namespace Upsilon.Binder -{ - public class VariableSymbol - { - public VariableSymbol(string name, Type type, bool local) - { - Type = type; - Local = local; - Name = name; - } - - public virtual Type Type { get; set; } - public bool Local { get; } - public string Name { get; } - public string[] CommentValue { get; set; } - - } - - public class FunctionVariableSymbol : VariableSymbol - { - public ImmutableArray Parameters { get; } - public Type ResultType { get; internal set; } - public bool IsBound { get; set; } - public bool IsInternal { get; } - - public FunctionVariableSymbol(string name, bool local, ImmutableArray parameters, Type resultType, bool isInternal) - : base(name, Type.Function, local) - { - Parameters = parameters; - ResultType = resultType; - IsInternal = isInternal; - } - } - - public class FunctionParameterSymbol : VariableSymbol - { - public BoundTypeDefinition BoundTypeDefinition { get; } - - public FunctionParameterSymbol(string name, Type type) : base(name, type, true) - { - - } - - public FunctionParameterSymbol(string name, BoundTypeDefinition type) : base(name, type.ScriptType, true) - { - BoundTypeDefinition = type; - } - - private Type _type; - public override Type Type - { - get => BoundTypeDefinition?.ScriptType ?? _type; - set => _type = Type; - } - } - - public class TableVariableSymbol : VariableSymbol - { - public Dictionary Variables { get; } - - public TableVariableSymbol(string name, bool local, Dictionary variables) - :base (name, Type.Table, local) - { - Variables = variables; - } - } -} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/FunctionParameterSymbol.cs b/Upsilon/Binder/VariableSymbols/FunctionParameterSymbol.cs new file mode 100644 index 0000000..9ede0df --- /dev/null +++ b/Upsilon/Binder/VariableSymbols/FunctionParameterSymbol.cs @@ -0,0 +1,27 @@ +using Upsilon.BaseTypes; +using Upsilon.BoundTypes; + +namespace Upsilon.Binder.VariableSymbols +{ + public class FunctionParameterSymbol : VariableSymbol + { + public BoundTypeDefinition BoundTypeDefinition { get; } + + public FunctionParameterSymbol(string name, Type type) : base(name, type, true) + { + + } + + public FunctionParameterSymbol(string name, BoundTypeDefinition type) : base(name, type.ScriptType, true) + { + BoundTypeDefinition = type; + } + + private Type _type; + public override Type Type + { + get => BoundTypeDefinition?.ScriptType ?? _type; + set => _type = Type; + } + } +} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs new file mode 100644 index 0000000..0e3581d --- /dev/null +++ b/Upsilon/Binder/VariableSymbols/FunctionVariableSymbol.cs @@ -0,0 +1,19 @@ +using System.Collections.Immutable; +using Upsilon.BaseTypes; + +namespace Upsilon.Binder.VariableSymbols +{ + public abstract class FunctionVariableSymbol : VariableSymbol + { + public Type ResultType { get; internal set; } + + public FunctionVariableSymbol(string name, bool local, Type resultType) + : base(name, Type.Function, local) + { + ResultType = resultType; + } + + public abstract (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters( + ImmutableArray callingParameters); + } +} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs new file mode 100644 index 0000000..8e8e540 --- /dev/null +++ b/Upsilon/Binder/VariableSymbols/InternalFunctionVariableSymbol.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Immutable; +using Type = Upsilon.BaseTypes.Type; + +namespace Upsilon.Binder.VariableSymbols +{ + public class InternalFunctionVariableSymbol : FunctionVariableSymbol + { + public class InternalFunctionParameter + { + public InternalFunctionParameter(Type t) + { + ValidTypes = t; + } + + public Type ValidTypes { get; set; } + } + + public InternalFunctionParameter[] FunctionParameters { get; } + + public InternalFunctionVariableSymbol(string name, bool local, Type resultType, InternalFunctionParameter[] functionParameters) + : base(name, local, resultType) + { + FunctionParameters = functionParameters; + } + + public override (bool IsValid, string Error, + BoundExpression WrongParameter) ValidateParameters(ImmutableArray callingParameters) + { + if (FunctionParameters.Length != callingParameters.Length) + { + return (false, + $"Invalid number of parameters for function '{Name}'. Expected {FunctionParameters.Length}, got {callingParameters.Length}", + null); + } + + for (var i = 0; i < FunctionParameters.Length; i++) + { + var functionParameter = FunctionParameters[i]; + var callingParameter = callingParameters[i]; + if (callingParameter.Type != Type.Unknown && callingParameter.Type != Type.Nil) + { + if ((functionParameter.ValidTypes ^ callingParameter.Type) != 0) + { + return (false, + $"Unexpected variable passed to internal function '{functionParameter}'.", + callingParameter); + } + } + } + + return (true, null, null); + } + } +} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs new file mode 100644 index 0000000..671780a --- /dev/null +++ b/Upsilon/Binder/VariableSymbols/ScriptFunctionVariableSymbol.cs @@ -0,0 +1,47 @@ +using System.Collections.Immutable; +using Upsilon.BaseTypes; + +namespace Upsilon.Binder.VariableSymbols +{ + public class ScriptFunctionVariableSymbol : FunctionVariableSymbol + { + public ImmutableArray Parameters { get; } + public bool IsBound { get; set; } + + + public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray parameters, Type resultType) + : base(name, local, resultType) + { + Parameters = parameters; + } + + public override (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(ImmutableArray callingParameters) + { + if (Parameters.Length != callingParameters.Length) + { + return (false, + $"Invalid number of parameters for function '{Name}'. Expected {Parameters.Length}, got {callingParameters.Length}", + null); + } + + for (var i = 0; i < Parameters.Length; i++) + { + var functionParameter = Parameters[i]; + var callingParameter = callingParameters[i]; + if (functionParameter.Type != Type.Unknown && + callingParameter.Type != Type.Unknown && callingParameter.Type != Type.Nil) + { + if (callingParameter.Type != functionParameter.Type) + { + return (false, $"Invalid type for function '{Name}' at parameter '{functionParameter.Name}'. " + + $"Expected type '{functionParameter.Type}', got '{callingParameter.Type}'", + callingParameter); + } + } + } + + return (true, null, null); + } + + } +} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/TableVariableSymbol.cs b/Upsilon/Binder/VariableSymbols/TableVariableSymbol.cs new file mode 100644 index 0000000..5560018 --- /dev/null +++ b/Upsilon/Binder/VariableSymbols/TableVariableSymbol.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; +using Upsilon.BaseTypes; + +namespace Upsilon.Binder.VariableSymbols +{ + public class TableVariableSymbol : VariableSymbol + { + public Dictionary Variables { get; } + + public TableVariableSymbol(string name, bool local, Dictionary variables) + :base (name, Type.Table, local) + { + Variables = variables; + } + } +} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbols/VariableSymbol.cs b/Upsilon/Binder/VariableSymbols/VariableSymbol.cs new file mode 100644 index 0000000..d046e5e --- /dev/null +++ b/Upsilon/Binder/VariableSymbols/VariableSymbol.cs @@ -0,0 +1,20 @@ +using Upsilon.BaseTypes; + +namespace Upsilon.Binder.VariableSymbols +{ + public class VariableSymbol + { + public VariableSymbol(string name, Type type, bool local) + { + Type = type; + Local = local; + Name = name; + } + + public virtual Type Type { get; set; } + public bool Local { get; } + public string Name { get; } + public string[] CommentValue { get; set; } + + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/EvaluationScope.cs b/Upsilon/Evaluator/EvaluationScope.cs index 4f73d27..ce4ddc9 100644 --- a/Upsilon/Evaluator/EvaluationScope.cs +++ b/Upsilon/Evaluator/EvaluationScope.cs @@ -2,6 +2,7 @@ using System; using System.Collections.Generic; using Upsilon.BaseTypes; using Upsilon.Binder; +using Upsilon.Binder.VariableSymbols; namespace Upsilon.Evaluator { diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 3f4424b..62ce0e5 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -6,6 +6,7 @@ using Upsilon.BaseTypes.ScriptFunction; using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BaseTypes.UserData; using Upsilon.Binder; +using Upsilon.Binder.VariableSymbols; using Type = Upsilon.BaseTypes.Type; namespace Upsilon.Evaluator diff --git a/Upsilon/StandardLibraries/StaticScope.cs b/Upsilon/StandardLibraries/StaticScope.cs index 9101eb9..7b6527f 100644 --- a/Upsilon/StandardLibraries/StaticScope.cs +++ b/Upsilon/StandardLibraries/StaticScope.cs @@ -1,10 +1,13 @@ using System; using System.Collections.Generic; -using System.Collections.Immutable; using System.Linq; using Upsilon.BaseTypes; +using Upsilon.BaseTypes.Number; +using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.Binder; +using Upsilon.Binder.VariableSymbols; using Upsilon.Evaluator; +using Type = Upsilon.BaseTypes.Type; namespace Upsilon.StandardLibraries { @@ -49,10 +52,11 @@ namespace Upsilon.StandardLibraries foreach (var func in basicFunctions) { funcs.Add(func.Key, func.Value.MethodInfoFunction); - var functionSymbol = new FunctionVariableSymbol(func.Key, true, ImmutableArray.Empty, - func.Value.MethodInfoFunction.ReturnType.GetScriptType(), true) + var functionSymbol = new InternalFunctionVariableSymbol(func.Key, true, + func.Value.MethodInfoFunction.ReturnType.GetScriptType(), + func.Value.MethodInfoFunction.GetParameterTypes().Select(x => + new InternalFunctionVariableSymbol.InternalFunctionParameter(DeriveValidTypes(x))).ToArray()) { - IsBound = true, CommentValue = func.Value.CommentValue?.Split('\n') }; boundFuncs.Add(func.Key, functionSymbol); @@ -69,5 +73,20 @@ namespace Upsilon.StandardLibraries BoundScope.AssignToNearest(varSymbol); Scope.AssignToNearest(varSymbol, luaVariable); } + + private static Type DeriveValidTypes(System.Type type) + { + if (type == typeof(ScriptString)) + return Type.String; + if (typeof(ScriptNumber).IsAssignableFrom(type)) + return Type.Number; + if (type == typeof(ScriptBoolean)) + return Type.Boolean; + if (type == typeof(IIterable)) + return Type.Table | Type.UserData; + if (typeof(ScriptTable).IsAssignableFrom(type)) + return Type.Table; + return Type.UserData; + } } } \ No newline at end of file diff --git a/Upsilon/Utilities/SearchHelper.cs b/Upsilon/Utilities/SearchHelper.cs index 3243118..338ca20 100644 --- a/Upsilon/Utilities/SearchHelper.cs +++ b/Upsilon/Utilities/SearchHelper.cs @@ -1,6 +1,7 @@ using System.Collections.Generic; using System.Linq; using Upsilon.Binder; +using Upsilon.Binder.VariableSymbols; namespace Upsilon.Utilities {