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; private readonly bool _passScriptReference;
public System.Type ReturnType { get; } 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) public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script)
{ {
var types = _directTypeManipulation var types = _directTypeManipulation

View File

@ -4,6 +4,7 @@ using System.Linq;
using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.Number;
using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Evaluator; using Upsilon.Evaluator;
using Upsilon.Text; using Upsilon.Text;

View File

@ -1,15 +1,18 @@
using System;
namespace Upsilon.BaseTypes namespace Upsilon.BaseTypes
{ {
public enum Type [Flags]
public enum Type : byte
{ {
Unknown, Unknown = 0,
Nil, Nil = 1,
Boolean, Boolean = 2,
Number, Number = 4,
String, String = 8,
Function, Function = 16,
UserData, UserData = 32,
Thread, Thread = 64,
Table, Table = 128,
} }
} }

View File

@ -6,7 +6,7 @@ namespace Upsilon.BaseTypes.UserData
{ {
public class UserDataMethod public class UserDataMethod
{ {
private class UserDataMethodPart public class UserDataMethodPart
{ {
public UserDataMethodPart(MethodInfo method) public UserDataMethodPart(MethodInfo method)
{ {
@ -18,7 +18,7 @@ namespace Upsilon.BaseTypes.UserData
public UserDataMethodParameter[] Parameters { get; } public UserDataMethodParameter[] Parameters { get; }
} }
private struct UserDataMethodParameter public struct UserDataMethodParameter
{ {
public UserDataMethodParameter(ParameterInfo info) public UserDataMethodParameter(ParameterInfo info)
{ {
@ -51,6 +51,11 @@ namespace Upsilon.BaseTypes.UserData
MethodParts.Add(part); MethodParts.Add(part);
} }
public List<UserDataMethodPart> GetMethods()
{
return MethodParts;
}
public MethodInfo GetMethod(System.Type[] parameterTypes) public MethodInfo GetMethod(System.Type[] parameterTypes)
{ {
foreach (var userDataMethodPart in MethodParts) foreach (var userDataMethodPart in MethodParts)

View File

@ -4,6 +4,7 @@ using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number; using Upsilon.BaseTypes.Number;
using Upsilon.Binder.VariableSymbols;
using Upsilon.BoundTypes; using Upsilon.BoundTypes;
using Upsilon.Parser; using Upsilon.Parser;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
@ -55,7 +56,7 @@ namespace Upsilon.Binder
var resultType = Scope.ReturnType; var resultType = Scope.ReturnType;
Scope = Scope.ParentScope; Scope = Scope.ParentScope;
var variable = var variable =
(FunctionVariableSymbol) unboundFunctionStatement.Value.Scope.ParentScope.Variables[ (ScriptFunctionVariableSymbol) unboundFunctionStatement.Value.Scope.ParentScope.Variables[
unboundFunctionStatement.Key]; unboundFunctionStatement.Key];
variable.IsBound = true; variable.IsBound = true;
variable.ResultType = resultType; variable.ResultType = resultType;
@ -209,59 +210,39 @@ namespace Upsilon.Binder
var returnType = Type.Unknown; var returnType = Type.Unknown;
if (ResolveVariable(expression) is FunctionVariableSymbol function) if (ResolveVariable(expression) is FunctionVariableSymbol function)
{ {
if (!function.IsBound) if (function is ScriptFunctionVariableSymbol scriptFunction)
{
if (!scriptFunction.IsBound)
{ {
Scope = new BoundScope(Scope); 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]; var callingVariable = parameters[index];
functionVariable.Type = callingVariable.Type; functionVariable.Type = callingVariable.Type;
Scope.DefineLocalVariable(functionVariable); Scope.DefineLocalVariable(functionVariable);
} }
var unboundFunctionStatement = _unboundFunctions[function.Name]; var unboundFunctionStatement = _unboundFunctions[scriptFunction.Name];
unboundFunctionStatement.Block = unboundFunctionStatement.Block =
(BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock); (BoundBlockStatement) BindBlockStatement(unboundFunctionStatement.UnboundBlock);
returnType = Scope.ReturnType; returnType = Scope.ReturnType;
Scope = Scope.ParentScope; Scope = Scope.ParentScope;
function.IsBound = true; scriptFunction.IsBound = true;
function.ResultType = returnType; scriptFunction.ResultType = returnType;
_unboundFunctions.Remove(function.Name); _unboundFunctions.Remove(scriptFunction.Name);
}
} }
returnType = function.ResultType; returnType = function.ResultType;
var pars = function.Parameters; var (isValid, error, wrongParameter) = function.ValidateParameters(parameters.ToImmutable());
if (!isValid)
if (!function.IsInternal)
{ {
if (pars.Length != parameters.Count) var span = e.Span;
{ if (wrongParameter != null)
_diagnostics.LogError( span = wrongParameter.Span;
$"Invalid number of parameters for function '{function.Name}'. Expected {pars.Length}, got {parameters.Count}", _diagnostics.LogError(error, span);
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);
}
}
}
}
} }
} }
return new BoundFunctionCallExpression(expression, parameters.ToImmutable(), e.Span, returnType); return new BoundFunctionCallExpression(expression, parameters.ToImmutable(), e.Span, returnType);
@ -532,7 +513,7 @@ namespace Upsilon.Binder
if (!Scope.TryGetVariable(name, !isLocal, out var variable)) 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() CommentValue = commentData.ToArray()
}; };

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
using System.Collections.Generic; using System.Collections.Generic;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text; using Upsilon.Text;
namespace Upsilon.Binder 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 System.Collections.Generic;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
namespace Upsilon.Evaluator namespace Upsilon.Evaluator
{ {

View File

@ -6,6 +6,7 @@ using Upsilon.BaseTypes.ScriptFunction;
using Upsilon.BaseTypes.ScriptTypeInterfaces; using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.BaseTypes.UserData; using Upsilon.BaseTypes.UserData;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.Evaluator namespace Upsilon.Evaluator

View File

@ -1,10 +1,13 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number;
using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Evaluator; using Upsilon.Evaluator;
using Type = Upsilon.BaseTypes.Type;
namespace Upsilon.StandardLibraries namespace Upsilon.StandardLibraries
{ {
@ -49,10 +52,11 @@ namespace Upsilon.StandardLibraries
foreach (var func in basicFunctions) foreach (var func in basicFunctions)
{ {
funcs.Add(func.Key, func.Value.MethodInfoFunction); funcs.Add(func.Key, func.Value.MethodInfoFunction);
var functionSymbol = new FunctionVariableSymbol(func.Key, true, ImmutableArray<VariableSymbol>.Empty, var functionSymbol = new InternalFunctionVariableSymbol(func.Key, true,
func.Value.MethodInfoFunction.ReturnType.GetScriptType(), 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') CommentValue = func.Value.CommentValue?.Split('\n')
}; };
boundFuncs.Add(func.Key, functionSymbol); boundFuncs.Add(func.Key, functionSymbol);
@ -69,5 +73,20 @@ namespace Upsilon.StandardLibraries
BoundScope.AssignToNearest(varSymbol); BoundScope.AssignToNearest(varSymbol);
Scope.AssignToNearest(varSymbol, luaVariable); 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.Collections.Generic;
using System.Linq; using System.Linq;
using Upsilon.Binder; using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
namespace Upsilon.Utilities namespace Upsilon.Utilities
{ {