Reworked function handling, support for type checking internal CSharp functions
This commit is contained in:
parent
81a6b05b26
commit
f08d1c2541
|
@ -24,6 +24,11 @@ namespace Upsilon.BaseTypes.ScriptFunction
|
|||
private readonly bool _passScriptReference;
|
||||
public System.Type ReturnType { get; }
|
||||
|
||||
public IEnumerable<System.Type> GetParameterTypes()
|
||||
{
|
||||
return _method.GetMethods().First().Parameters.Select(x => x.Type);
|
||||
}
|
||||
|
||||
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script)
|
||||
{
|
||||
var types = _directTypeManipulation
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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,
|
||||
}
|
||||
}
|
|
@ -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<UserDataMethodPart> GetMethods()
|
||||
{
|
||||
return MethodParts;
|
||||
}
|
||||
|
||||
public MethodInfo GetMethod(System.Type[] parameterTypes)
|
||||
{
|
||||
foreach (var userDataMethodPart in MethodParts)
|
||||
|
|
|
@ -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()
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
using Type = Upsilon.BaseTypes.Type;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Collections.Immutable;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
using Upsilon.Text;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
|
|
|
@ -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<VariableSymbol> Parameters { get; }
|
||||
public Type ResultType { get; internal set; }
|
||||
public bool IsBound { get; set; }
|
||||
public bool IsInternal { get; }
|
||||
|
||||
public FunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> 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<string, VariableSymbol> Variables { get; }
|
||||
|
||||
public TableVariableSymbol(string name, bool local, Dictionary<string, VariableSymbol> variables)
|
||||
:base (name, Type.Table, local)
|
||||
{
|
||||
Variables = variables;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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<BoundExpression> callingParameters);
|
||||
}
|
||||
}
|
|
@ -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<BoundExpression> 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);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
using System.Collections.Immutable;
|
||||
using Upsilon.BaseTypes;
|
||||
|
||||
namespace Upsilon.Binder.VariableSymbols
|
||||
{
|
||||
public class ScriptFunctionVariableSymbol : FunctionVariableSymbol
|
||||
{
|
||||
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||
public bool IsBound { get; set; }
|
||||
|
||||
|
||||
public ScriptFunctionVariableSymbol(string name, bool local, ImmutableArray<VariableSymbol> parameters, Type resultType)
|
||||
: base(name, local, resultType)
|
||||
{
|
||||
Parameters = parameters;
|
||||
}
|
||||
|
||||
public override (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(ImmutableArray<BoundExpression> 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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
|
||||
namespace Upsilon.Binder.VariableSymbols
|
||||
{
|
||||
public class TableVariableSymbol : VariableSymbol
|
||||
{
|
||||
public Dictionary<string, VariableSymbol> Variables { get; }
|
||||
|
||||
public TableVariableSymbol(string name, bool local, Dictionary<string, VariableSymbol> variables)
|
||||
:base (name, Type.Table, local)
|
||||
{
|
||||
Variables = variables;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
||||
}
|
||||
}
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.Collections.Generic;
|
||||
using Upsilon.BaseTypes;
|
||||
using Upsilon.Binder;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
|
||||
namespace Upsilon.Evaluator
|
||||
{
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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<VariableSymbol>.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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,6 +1,7 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Upsilon.Binder;
|
||||
using Upsilon.Binder.VariableSymbols;
|
||||
|
||||
namespace Upsilon.Utilities
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue