Reworked function handling, support for type checking internal CSharp functions

This commit is contained in:
Deukhoofd 2018-11-30 15:28:36 +01:00
parent 81a6b05b26
commit f08d1c2541
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
23 changed files with 271 additions and 135 deletions

View File

@ -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

View File

@ -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;

View File

@ -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,
}
}

View File

@ -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)

View File

@ -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)
{
if (!scriptFunction.IsBound)
{
Scope = new BoundScope(Scope);
for (var index = 0; index < function.Parameters.Length; index++)
for (var index = 0; index < scriptFunction.Parameters.Length; index++)
{
var functionVariable = function.Parameters[index];
var functionVariable = scriptFunction.Parameters[index];
var callingVariable = parameters[index];
functionVariable.Type = callingVariable.Type;
Scope.DefineLocalVariable(functionVariable);
}
var unboundFunctionStatement = _unboundFunctions[function.Name];
var unboundFunctionStatement = _unboundFunctions[scriptFunction.Name];
unboundFunctionStatement.Block =
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock);
returnType = Scope.ReturnType;
Scope = Scope.ParentScope;
function.IsBound = true;
function.ResultType = returnType;
_unboundFunctions.Remove(function.Name);
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()
};

View File

@ -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

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Upsilon.Binder.VariableSymbols;
using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.Binder

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text;
namespace Upsilon.Binder

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text;
namespace Upsilon.Binder

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using System.Collections.Immutable;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text;
namespace Upsilon.Binder

View File

@ -1,4 +1,5 @@
using System.Collections.Generic;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text;
namespace Upsilon.Binder

View File

@ -1,5 +1,6 @@
using System.Collections.Generic;
using Upsilon.BaseTypes;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text;
namespace Upsilon.Binder

View File

@ -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;
}
}
}

View File

@ -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;
}
}
}

View File

@ -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);
}
}

View File

@ -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);
}
}
}

View File

@ -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);
}
}
}

View File

@ -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;
}
}
}

View File

@ -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; }
}
}

View File

@ -2,6 +2,7 @@ using System;
using System.Collections.Generic;
using Upsilon.BaseTypes;
using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
namespace Upsilon.Evaluator
{

View File

@ -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

View File

@ -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;
}
}
}

View File

@ -1,6 +1,7 @@
using System.Collections.Generic;
using System.Linq;
using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
namespace Upsilon.Utilities
{