Rework type binding to allow for type awareness in iterators

This commit is contained in:
Deukhoofd 2019-01-18 16:09:25 +01:00
parent 64aedceb85
commit f55e6d314d
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
27 changed files with 286 additions and 125 deletions

View File

@ -15,13 +15,13 @@ namespace Upsilon.BaseTypes.ScriptFunction
public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation, public ScriptMethodInfoFunction(UserDataMethod method, object o, bool directTypeManipulation,
bool passScriptReference = false, bool passScopeReference = false) bool passScriptReference = false, bool passScopeReference = false)
{ {
_method = method; Method = method;
_object = o; _object = o;
_directTypeManipulation = directTypeManipulation; _directTypeManipulation = directTypeManipulation;
_passScriptReference = passScriptReference; _passScriptReference = passScriptReference;
_passScopeReference = passScopeReference; _passScopeReference = passScopeReference;
ReturnType = _method.ReturnType; ReturnType = Method.ReturnType;
if (method.GetMethods().First().Attribute != null) if (method.GetMethods().First().Attribute != null)
{ {
@ -33,7 +33,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
} }
} }
private readonly UserDataMethod _method; public UserDataMethod Method { get; }
private readonly object _object; private readonly object _object;
private readonly bool _directTypeManipulation; private readonly bool _directTypeManipulation;
private readonly bool _passScriptReference; private readonly bool _passScriptReference;
@ -42,7 +42,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
public IEnumerable<UserDataMethod.UserDataMethodParameter> GetParameterInfo() public IEnumerable<UserDataMethod.UserDataMethodParameter> GetParameterInfo()
{ {
return _method.GetMethods().First().Parameters; return Method.GetMethods().First().Parameters;
} }
public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span) public override ScriptType Run(Diagnostics diagnostics, ScriptType[] variables, Script script, EvaluationScope scope, TextSpan span)
@ -61,7 +61,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
{ {
// grab the parameters, and just invoke it // grab the parameters, and just invoke it
var array = objects.ToArray(); var array = objects.ToArray();
var methodInfo = _method.GetMethod(ref array); var methodInfo = Method.GetMethod(ref array);
result = methodInfo.Invoke(null, array); result = methodInfo.Invoke(null, array);
} }
else else
@ -90,7 +90,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
foreach (var match in methods) foreach (var match in methods)
{ {
// if the method does not have the name we're looking for, continue // if the method does not have the name we're looking for, continue
if (!string.Equals(match.Name, _method.Name)) continue; if (!string.Equals(match.Name, Method.Name)) continue;
// Use the type binder to check if the match is allowed // Use the type binder to check if the match is allowed
if (UpsilonBinder.Default.IsValidMatch(match, convertedTypes)) if (UpsilonBinder.Default.IsValidMatch(match, convertedTypes))
{ {
@ -108,7 +108,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
if (method == null) if (method == null)
{ {
throw new ScriptRuntimeException( throw new ScriptRuntimeException(
$"Can't find method {_method.Name} with parameter types {string.Join(", ", convertedTypes.Select(x => x.Name))} on type {_object.GetType().Name}", $"Can't find method {Method.Name} with parameter types {string.Join(", ", convertedTypes.Select(x => x.Name))} on type {_object.GetType().Name}",
span, diagnostics.ScriptString.GetSpan(span)); span, diagnostics.ScriptString.GetSpan(span));
} }
// get the method parameters // get the method parameters

View File

@ -0,0 +1,65 @@
using System.Collections.Immutable;
namespace Upsilon.BaseTypes
{
public class TypeContainer
{
public Type Type { get; }
protected TypeContainer(Type t)
{
Type = t;
}
public static implicit operator TypeContainer (Type type)
{
return new TypeContainer(type);
}
public static implicit operator Type(TypeContainer t)
{
return t.Type;
}
protected bool Equals(TypeContainer other)
{
return Type == other.Type;
}
public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj is Type t)
{
return t == Type;
}
if (obj.GetType() != this.GetType()) return false;
return Equals((TypeContainer) obj);
}
public override int GetHashCode()
{
return (int) Type;
}
public static bool operator == (TypeContainer a, object b)
{
return Equals(a, b);
}
public static bool operator != (TypeContainer a, object b)
{
return !Equals(a, b);
}
}
public class CompositeTypeContainer : TypeContainer
{
public ImmutableArray<Type> Types { get; set; }
public CompositeTypeContainer() : base(Type.Table)
{
}
}
}

View File

@ -55,7 +55,7 @@ namespace Upsilon.Binder
foreach (var valueParameter in unboundFunctionStatement.Value.Parameters) foreach (var valueParameter in unboundFunctionStatement.Value.Parameters)
{ {
Scope.AssignToNearest(valueParameter.VariableSymbol); Scope.AssignToNearest(valueParameter.VariableSymbol);
if (valueParameter.VariableSymbol.Type == Type.Unknown) if (valueParameter.VariableSymbol.TypeContainer == Type.Unknown)
_diagnostics.LogUnknownVariableType(valueParameter.VariableSymbol.Name, valueParameter.Span); _diagnostics.LogUnknownVariableType(valueParameter.VariableSymbol.Name, valueParameter.Span);
} }
unboundFunctionStatement.Value.Block = unboundFunctionStatement.Value.Block =
@ -220,13 +220,14 @@ namespace Upsilon.Binder
parameters.Add(bound); parameters.Add(bound);
} }
var returnType = Type.Unknown; TypeContainer returnType = Type.Unknown;
var resolved = ResolveVariable(expression, _diagnostics); var resolved = ResolveVariable(expression, _diagnostics);
if (resolved == null) if (resolved == null)
{ {
_diagnostics.LogError("Can't resolve variable", expression.Span); _diagnostics.LogError("Can't resolve variable", expression.Span);
} }
if (resolved is FunctionVariableSymbol function)
else if (resolved is FunctionVariableSymbol function)
{ {
if (function is ScriptFunctionVariableSymbol scriptFunction) if (function is ScriptFunctionVariableSymbol scriptFunction)
{ {
@ -237,7 +238,7 @@ namespace Upsilon.Binder
{ {
var functionVariable = scriptFunction.Parameters[index]; var functionVariable = scriptFunction.Parameters[index];
var callingVariable = parameters[index]; var callingVariable = parameters[index];
functionVariable.Type = callingVariable.Type; functionVariable.TypeContainer = callingVariable.Type;
Scope.DefineLocalVariable(functionVariable); Scope.DefineLocalVariable(functionVariable);
} }
@ -280,10 +281,16 @@ namespace Upsilon.Binder
} }
} }
} }
if (resolved is InternalFunctionVariableSymbol internalFunction)
{
returnType = internalFunction.GetResultType(parameters.ToArray());
}
else
{
returnType = function.ResultType;
}
} }
returnType = function.ResultType;
var (isValid, error, wrongParameter) = function.ValidateParameters(parameters.ToImmutable()); var (isValid, error, wrongParameter) = function.ValidateParameters(parameters.ToImmutable());
if (!isValid) if (!isValid)
{ {
@ -331,11 +338,11 @@ namespace Upsilon.Binder
{ {
diagnostics?.LogError("Can't resolve variable", expression.Span); diagnostics?.LogError("Can't resolve variable", expression.Span);
} }
else if (indexerVariable.Type == Type.Table) else if (indexerVariable.TypeContainer == Type.Table)
{ {
return ((TableVariableSymbol)indexerVariable).Variables[fullStopIndexExpression.Index]; return ((TableVariableSymbol)indexerVariable).Variables[fullStopIndexExpression.Index];
} }
else if (indexerVariable.Type == Type.UserData) else if (indexerVariable.TypeContainer == Type.UserData)
{ {
var parent = var parent =
(UserDataBoundTypeDefinition) ((UserDataVariableSymbol) indexerVariable).BoundTypeDefinition; (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) indexerVariable).BoundTypeDefinition;
@ -349,7 +356,7 @@ namespace Upsilon.Binder
} }
} }
} }
else if (indexerVariable.Type == Type.Unknown) else if (indexerVariable.TypeContainer == Type.Unknown)
{ {
if (indexerVariable is UserDataVariableSymbol funcSymbol) if (indexerVariable is UserDataVariableSymbol funcSymbol)
{ {
@ -405,17 +412,18 @@ namespace Upsilon.Binder
{ {
if (assignment.Kind == BoundKind.BoundTableExpression) if (assignment.Kind == BoundKind.BoundTableExpression)
{ {
variable = new TableVariableSymbol(name, isLocal, ((BoundTableExpression)assignment).Expressions); variable = new TableVariableSymbol(name, isLocal,
((BoundTableExpression) assignment).Expressions, assignment.Type);
} }
else if (assignment.Kind == BoundKind.VariableExpression) else if (assignment.Kind == BoundKind.VariableExpression)
{ {
variable = new TableVariableSymbol(name, isLocal, variable = new TableVariableSymbol(name, isLocal,
((TableVariableSymbol) ((BoundVariableExpression) assignment).Variable.VariableSymbol) ((TableVariableSymbol) ((BoundVariableExpression) assignment).Variable.VariableSymbol)
.Variables); .Variables, assignment.Type);
} }
else else
{ {
variable = new TableVariableSymbol(name, isLocal); variable = new TableVariableSymbol(name, isLocal, assignment.Type);
} }
} }
else else
@ -435,26 +443,26 @@ namespace Upsilon.Binder
else else
{ {
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables // don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
if (assignment.Type != variable.Type) if (assignment.Type != variable.TypeContainer)
{ {
if (variable.Type == Type.Nil || assignment.Type == Type.Nil) if (variable.TypeContainer == Type.Nil || assignment.Type == Type.Nil)
{ {
variable.Type = assignment.Type; variable.TypeContainer = assignment.Type;
} }
else if (variable.Type == Type.Unknown) else if (variable.TypeContainer == Type.Unknown)
{ {
variable.Type = assignment.Type; variable.TypeContainer = assignment.Type;
} }
else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v) else if (assignment.Type == Type.Unknown && assignment is BoundVariableExpression v)
{ {
v.Variable.VariableSymbol.Type = variable.Type; v.Variable.VariableSymbol.TypeContainer = variable.TypeContainer;
} }
else if (assignment.Type == Type.Unknown) else if (assignment.Type == Type.Unknown)
{ {
} }
else else
{ {
_diagnostics.LogCannotConvert(assignment.Type, variable.Type, assignment.Span); _diagnostics.LogCannotConvert(assignment.Type, variable.TypeContainer, assignment.Span);
return (null, false); return (null, false);
} }
} }
@ -474,7 +482,7 @@ namespace Upsilon.Binder
var (symbol, isCreation) = TryBindVariable(name, isLocal, boundExpression, e.CommentData); var (symbol, isCreation) = TryBindVariable(name, isLocal, boundExpression, e.CommentData);
if (symbol != null) if (symbol != null)
{ {
if (symbol.Type == Type.Unknown) if (symbol.TypeContainer == Type.Unknown)
{ {
_diagnostics.LogUnknownVariableType(symbol.Name, variableExpression.Span); _diagnostics.LogUnknownVariableType(symbol.Name, variableExpression.Span);
} }
@ -496,7 +504,7 @@ namespace Upsilon.Binder
foreach (var identifierToken in s.Identifiers) foreach (var identifierToken in s.Identifiers)
{ {
var boundVariable = TryBindVariable(identifierToken.Name, isLocal, assignment, null); var boundVariable = TryBindVariable(identifierToken.Name, isLocal, assignment, null);
if (boundVariable.Symbol.Type == Type.Unknown) if (boundVariable.Symbol.TypeContainer == Type.Unknown)
{ {
_diagnostics.LogUnknownVariableType(boundVariable.Symbol.Name, identifierToken.Span); _diagnostics.LogUnknownVariableType(boundVariable.Symbol.Name, identifierToken.Span);
} }
@ -610,7 +618,7 @@ namespace Upsilon.Binder
foreach (var parameter in func.Parameters) foreach (var parameter in func.Parameters)
{ {
var vari = new VariableSymbol(parameter.VariableSymbol.Name, parameter.VariableSymbol.Type, true); var vari = new VariableSymbol(parameter.VariableSymbol.Name, parameter.VariableSymbol.TypeContainer, true);
parameters.Add(vari); parameters.Add(vari);
innerScope.DefineLocalVariable(vari); innerScope.DefineLocalVariable(vari);
} }
@ -637,15 +645,15 @@ namespace Upsilon.Binder
else else
{ {
// don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables // don't allow assigning different typed variables to a variable, unless either of them is nil, allow assigning nil to all variables
if (variable.Type != Type.Function) if (variable.TypeContainer != Type.Function)
{ {
if (variable.Type == Type.Nil || variable.Type == Type.Unknown) if (variable.TypeContainer == Type.Nil || variable.TypeContainer == Type.Unknown)
{ {
variable.Type = Type.Function; variable.TypeContainer = Type.Function;
} }
else else
{ {
_diagnostics.LogCannotConvert(Type.Function, variable.Type, e.Span); _diagnostics.LogCannotConvert(Type.Function, variable.TypeContainer, e.Span);
return new BoundExpressionStatement(new BoundBadExpression(e.Span), e.Span); return new BoundExpressionStatement(new BoundBadExpression(e.Span), e.Span);
} }
} }
@ -712,7 +720,7 @@ namespace Upsilon.Binder
_diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span); _diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span);
return new BoundBadExpression(e.Span); return new BoundBadExpression(e.Span);
} }
switch (expression.Type) switch (expression.Type.Type)
{ {
case Type.Table: case Type.Table:
if (isAssignment) if (isAssignment)
@ -726,7 +734,7 @@ namespace Upsilon.Binder
var variableDic = table.Expressions; var variableDic = table.Expressions;
if (variableDic.TryGetValue(realIndex.Value.ToString(), out var variable)) if (variableDic.TryGetValue(realIndex.Value.ToString(), out var variable))
{ {
return new BoundIndexExpression(expression, index, variable.Type, e.Span); return new BoundIndexExpression(expression, index, variable.TypeContainer, e.Span);
} }
_diagnostics.LogError($"No variable '{realIndex.Value}' found in table.", _diagnostics.LogError($"No variable '{realIndex.Value}' found in table.",
@ -746,7 +754,7 @@ namespace Upsilon.Binder
var variableDic = tableSymbol.Variables; var variableDic = tableSymbol.Variables;
if (variableDic.TryGetValue(realIndex.Value.ToString(), out var variable)) if (variableDic.TryGetValue(realIndex.Value.ToString(), out var variable))
{ {
return new BoundIndexExpression(expression, index, variable.Type, e.Span); return new BoundIndexExpression(expression, index, variable.TypeContainer, e.Span);
} }
_diagnostics.LogError($"No variable '{realIndex.Value}' found in table '{realTable.VariableSymbol.Name}'.", _diagnostics.LogError($"No variable '{realIndex.Value}' found in table '{realTable.VariableSymbol.Name}'.",
@ -768,7 +776,7 @@ namespace Upsilon.Binder
{ {
var expression = BindExpression(e.Expression); var expression = BindExpression(e.Expression);
var index = e.Index.Name; var index = e.Index.Name;
switch (expression.Type) switch (expression.Type.Type)
{ {
case Type.Table: case Type.Table:
if (isAssignment) if (isAssignment)
@ -780,7 +788,7 @@ namespace Upsilon.Binder
var table = (BoundTableExpression)expression; var table = (BoundTableExpression)expression;
if (table.Expressions.TryGetValue(index, out var variable)) if (table.Expressions.TryGetValue(index, out var variable))
{ {
return new BoundFullStopIndexExpression(expression, index, variable.Type, e.Span); return new BoundFullStopIndexExpression(expression, index, variable.TypeContainer, e.Span);
} }
_diagnostics.LogError($"No variable '{index}' found in table.", e.Span); _diagnostics.LogError($"No variable '{index}' found in table.", e.Span);
} }
@ -791,7 +799,7 @@ namespace Upsilon.Binder
var variableDic = ((TableVariableSymbol) realTable.VariableSymbol).Variables; var variableDic = ((TableVariableSymbol) realTable.VariableSymbol).Variables;
if (variableDic.TryGetValue(index, out var variable)) if (variableDic.TryGetValue(index, out var variable))
{ {
return new BoundFullStopIndexExpression(expression, index, variable.Type, e.Span); return new BoundFullStopIndexExpression(expression, index, variable.TypeContainer, e.Span);
} }
_diagnostics.LogError($"No variable '{index}' found in table '{realTable.VariableSymbol.Name}'.", _diagnostics.LogError($"No variable '{index}' found in table '{realTable.VariableSymbol.Name}'.",
@ -897,13 +905,29 @@ namespace Upsilon.Binder
{ {
Scope = new BoundScope(Scope); Scope = new BoundScope(Scope);
var array = ImmutableArray.CreateBuilder<BoundVariableSymbol>(); var array = ImmutableArray.CreateBuilder<BoundVariableSymbol>();
foreach (var variableIdentifier in e.Variables)
{ var keyVar = e.Variables[0];
var variable = new VariableSymbol(variableIdentifier.Name, Type.Unknown, true); var keyVariable = new VariableSymbol(keyVar.Name, Type.String, true);
Scope.DefineLocalVariable(variable); Scope.DefineLocalVariable(keyVariable);
array.Add(new BoundVariableSymbol(variable, true, variableIdentifier.Span)); array.Add(new BoundVariableSymbol(keyVariable, true, keyVar.Span));
}
var boundEnumerableExpression = BindExpression(e.EnumerableExpression); var boundEnumerableExpression = BindExpression(e.EnumerableExpression);
var valueVar = e.Variables[1];
VariableSymbol valueVariable;
if (boundEnumerableExpression.Type is CompositeTypeContainer composite && composite.Types.Length == 2)
{
valueVariable = new VariableSymbol(valueVar.Name, composite.Types[1], true);
}
else
{
throw new Exception();
valueVariable = new VariableSymbol(valueVar.Name, Type.Unknown, true);
}
Scope.DefineLocalVariable(valueVariable);
array.Add(new BoundVariableSymbol(valueVariable, true, valueVar.Span));
var block = BindBlockStatement(e.Block); var block = BindBlockStatement(e.Block);
return new BoundGenericForStatement(array.ToImmutable(), boundEnumerableExpression, block, e.Span); return new BoundGenericForStatement(array.ToImmutable(), boundEnumerableExpression, block, e.Span);

View File

@ -17,6 +17,6 @@ namespace Upsilon.Binder
yield break; yield break;
} }
public override Type Type => Type.Nil; public override TypeContainer Type => BaseTypes.Type.Nil;
} }
} }

View File

@ -23,7 +23,7 @@ namespace Upsilon.Binder
yield return RightExpression; yield return RightExpression;
} }
public override Type Type { get; } public override TypeContainer Type { get; }
public BoundBinaryOperator Operator { get; } public BoundBinaryOperator Operator { get; }
public BoundExpression LeftExpression { get; } public BoundExpression LeftExpression { get; }

View File

@ -9,6 +9,6 @@ namespace Upsilon.Binder
{ {
} }
public abstract Type Type { get; } public abstract TypeContainer Type { get; }
} }
} }

View File

@ -11,7 +11,7 @@ namespace Upsilon.Binder
public ImmutableArray<BoundExpression> Parameters { get; } public ImmutableArray<BoundExpression> Parameters { get; }
public BoundFunctionCallExpression(BoundExpression identifier, ImmutableArray<BoundExpression> parameters, public BoundFunctionCallExpression(BoundExpression identifier, ImmutableArray<BoundExpression> parameters,
TextSpan span, Type type) : base(span) TextSpan span, TypeContainer type) : base(span)
{ {
Identifier = identifier; Identifier = identifier;
Parameters = parameters; Parameters = parameters;
@ -29,6 +29,6 @@ namespace Upsilon.Binder
} }
} }
public override Type Type { get; } public override TypeContainer Type { get; }
} }
} }

View File

@ -1,6 +1,7 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using Upsilon.BaseTypes;
using Upsilon.Text; using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
@ -31,7 +32,7 @@ namespace Upsilon.Binder
yield return Block; yield return Block;
} }
public override Type Type => Type.Function; public override TypeContainer Type => BaseTypes.Type.Function;
public BoundScope Scope { get; set; } public BoundScope Scope { get; set; }
public Type ReturnType { get; } public Type ReturnType { get; }
} }

View File

@ -25,7 +25,7 @@ namespace Upsilon.Binder
yield return Index; yield return Index;
} }
public override Type Type { get; } public override TypeContainer Type { get; }
} }
public class BoundFullStopIndexExpression : BoundExpression public class BoundFullStopIndexExpression : BoundExpression
@ -47,6 +47,6 @@ namespace Upsilon.Binder
yield return Expression; yield return Expression;
} }
public override Type Type { get; } public override TypeContainer Type { get; }
} }
} }

View File

@ -18,7 +18,7 @@ namespace Upsilon.Binder
yield break; yield break;
} }
public override Type Type => Value.Type; public override TypeContainer Type => Value.Type;
public ScriptType Value { get; } public ScriptType Value { get; }
} }
} }

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using Upsilon.BaseTypes;
using Upsilon.Binder.VariableSymbols; using Upsilon.Binder.VariableSymbols;
using Upsilon.Text; using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
@ -24,7 +24,39 @@ namespace Upsilon.Binder
return Statements; return Statements;
} }
public override Type Type => Type.Table; public override TypeContainer Type
{
get
{
Type? valueType = null;
foreach (var statement in Statements)
{
if (!(statement is BoundExpressionStatement exp))
{
valueType = BaseTypes.Type.Unknown;
break;
}
if (!valueType.HasValue)
{
valueType = exp.Expression.Type;
continue;
}
if (valueType == exp.Expression.Type) continue;
valueType = BaseTypes.Type.Unknown;
break;
}
var valueRealType = BaseTypes.Type.Unknown;
if (valueType.HasValue)
valueRealType = valueType.Value;
var arr = new[] {BaseTypes.Type.String, valueRealType};
return new CompositeTypeContainer()
{
Types = arr.ToImmutableArray()
};
}
}
public Dictionary<string, VariableSymbol> Expressions { get; } public Dictionary<string, VariableSymbol> Expressions { get; }

View File

@ -13,7 +13,7 @@ namespace Upsilon.Binder
yield return InExpression; yield return InExpression;
} }
public override Type Type { get; } public override TypeContainer Type { get; }
public BoundUnaryExpression(BoundUnaryOperator op, BoundExpression inExpression, Type type, TextSpan span) : base(span) public BoundUnaryExpression(BoundUnaryOperator op, BoundExpression inExpression, Type type, TextSpan span) : base(span)
{ {

View File

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using Upsilon.BaseTypes;
using Upsilon.Text; using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
@ -20,6 +21,6 @@ namespace Upsilon.Binder
yield break; yield break;
} }
public override Type Type => Variable.Type; public override TypeContainer Type => Variable.Type;
} }
} }

View File

@ -10,7 +10,7 @@ namespace Upsilon.Binder
{ {
public UnboundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters, public UnboundFunctionExpression(ImmutableArray<BoundVariableSymbol> parameters,
BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope) BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope)
: base(parameters, null, span, scope, Type.Unknown) : base(parameters, null, span, scope, BaseTypes.Type.Unknown)
{ {
UnboundBlock = unboundBlock; UnboundBlock = unboundBlock;
} }

View File

@ -27,6 +27,6 @@ namespace Upsilon.Binder
yield break; yield break;
} }
public override Type Type => VariableSymbol.Type; public override TypeContainer Type => VariableSymbol.TypeContainer;
} }
} }

View File

@ -8,18 +8,12 @@ namespace Upsilon.Binder.VariableSymbols
public Type ResultType { get; internal set; } public Type ResultType { get; internal set; }
public FunctionVariableSymbol(string name, bool local, Type resultType) public FunctionVariableSymbol(string name, bool local, Type resultType)
: base(name, Type.Function, local) : base(name, BaseTypes.Type.Function, local)
{ {
ResultType = resultType; ResultType = resultType;
} }
public abstract (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters( public abstract (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(
ImmutableArray<BoundExpression> callingParameters); ImmutableArray<BoundExpression> callingParameters);
public override Type Type
{
get => Type.Function;
set{}
}
} }
} }

View File

@ -1,6 +1,7 @@
using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection;
using Upsilon.BaseTypes;
using Upsilon.BoundTypes; using Upsilon.BoundTypes;
using Type = Upsilon.BaseTypes.Type; using Type = Upsilon.BaseTypes.Type;
@ -8,37 +9,17 @@ namespace Upsilon.Binder.VariableSymbols
{ {
public class InternalFunctionVariableSymbol : FunctionVariableSymbol public class InternalFunctionVariableSymbol : FunctionVariableSymbol
{ {
public class InternalFunctionParameter private InternalFunctionParameter[] FunctionParameters { get; }
{
public InternalFunctionParameter(string name, Type type, bool isOptional)
{
ValidTypes = type;
IsOptional = isOptional;
Name = name;
}
public InternalFunctionParameter(string name, Type type, string[] expectedUserData, bool isOptional)
{
ValidTypes = type;
ExpectedUserData = expectedUserData;
IsOptional = isOptional;
Name = name;
}
public string Name { get; }
public Type ValidTypes { get; }
public string[] ExpectedUserData { get; }
public bool IsOptional { get; }
}
public InternalFunctionParameter[] FunctionParameters { get; }
private int MinimalParametersRequired { get; } private int MinimalParametersRequired { get; }
private MethodInfo OverrideResultType { get; }
public InternalFunctionVariableSymbol(string name, bool local, Type resultType, InternalFunctionParameter[] functionParameters) public InternalFunctionVariableSymbol(string name, bool local, Type resultType,
InternalFunctionParameter[] functionParameters, MethodInfo overrideResultType)
: base(name, local, resultType) : base(name, local, resultType)
{ {
FunctionParameters = functionParameters; FunctionParameters = functionParameters;
MinimalParametersRequired = functionParameters.Count(x => !x.IsOptional); MinimalParametersRequired = functionParameters.Count(x => !x.IsOptional);
OverrideResultType = overrideResultType;
} }
public override (bool IsValid, string Error, public override (bool IsValid, string Error,
@ -70,7 +51,7 @@ namespace Upsilon.Binder.VariableSymbols
if (functionParameter.ValidTypes.HasFlag(Type.UserData)) if (functionParameter.ValidTypes.HasFlag(Type.UserData))
{ {
var variable = Binder.ResolveVariable(callingParameter, null); var variable = Binder.ResolveVariable(callingParameter, null);
if (variable != null && variable.Type == Type.UserData) if (variable != null && variable.TypeContainer == Type.UserData)
{ {
var parent = var parent =
(UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition; (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition;
@ -87,5 +68,35 @@ namespace Upsilon.Binder.VariableSymbols
return (true, null, null); return (true, null, null);
} }
public TypeContainer GetResultType(BoundExpression[] parameters)
{
if (OverrideResultType == null)
return ResultType;
return (TypeContainer) OverrideResultType.Invoke(null, new object[] {parameters});
}
public class InternalFunctionParameter
{
public InternalFunctionParameter(string name, TypeContainer type, bool isOptional)
{
ValidTypes = type;
IsOptional = isOptional;
Name = name;
}
public InternalFunctionParameter(string name, TypeContainer type, string[] expectedUserData, bool isOptional)
{
ValidTypes = type;
ExpectedUserData = expectedUserData;
IsOptional = isOptional;
Name = name;
}
public string Name { get; }
public Type ValidTypes { get; }
public string[] ExpectedUserData { get; }
public bool IsOptional { get; }
}
} }
} }

View File

@ -28,13 +28,13 @@ namespace Upsilon.Binder.VariableSymbols
{ {
var functionParameter = Parameters[i]; var functionParameter = Parameters[i];
var callingParameter = callingParameters[i]; var callingParameter = callingParameters[i];
if (functionParameter.Type != Type.Unknown && if (functionParameter.TypeContainer != BaseTypes.Type.Unknown &&
callingParameter.Type != Type.Unknown && callingParameter.Type != Type.Nil) callingParameter.Type != BaseTypes.Type.Unknown && callingParameter.Type != BaseTypes.Type.Nil)
{ {
if (callingParameter.Type != functionParameter.Type) if (callingParameter.Type != functionParameter.TypeContainer)
{ {
return (false, $"Invalid type for function '{Name}' at parameter '{functionParameter.Name}'. " + return (false, $"Invalid type for function '{Name}' at parameter '{functionParameter.Name}'. " +
$"Expected type '{functionParameter.Type}', got '{callingParameter.Type}'", $"Expected type '{functionParameter.TypeContainer}', got '{callingParameter.Type}'",
callingParameter); callingParameter);
} }
} }

View File

@ -7,15 +7,16 @@ namespace Upsilon.Binder.VariableSymbols
{ {
public Dictionary<string, VariableSymbol> Variables { get; } public Dictionary<string, VariableSymbol> Variables { get; }
public bool ContentAware { get; } public bool ContentAware { get; }
public TableVariableSymbol(string name, bool local, Dictionary<string, VariableSymbol> variables) public TableVariableSymbol(string name, bool local, Dictionary<string, VariableSymbol> variables,
:base (name, Type.Table, local) TypeContainer type)
:base (name, type, local)
{ {
Variables = variables; Variables = variables;
ContentAware = true; ContentAware = true;
} }
public TableVariableSymbol(string name, bool local) public TableVariableSymbol(string name, bool local, TypeContainer type)
:base (name, Type.Table, local) :base (name, type, local)
{ {
Variables = new Dictionary<string, VariableSymbol>(); Variables = new Dictionary<string, VariableSymbol>();
ContentAware = false; ContentAware = false;

View File

@ -8,9 +8,9 @@ namespace Upsilon.Binder.VariableSymbols
public BoundTypeDefinition BoundTypeDefinition { get; } public BoundTypeDefinition BoundTypeDefinition { get; }
public UserDataBoundTypeDefinition Parent { get; } public UserDataBoundTypeDefinition Parent { get; }
public UserDataVariableSymbol(string name, Type type) : base(name, type, true) public UserDataVariableSymbol(string name, Type typeContainer) : base(name, typeContainer, true)
{ {
_type = type; _typeContainer = typeContainer;
} }
public UserDataVariableSymbol(string name, BoundTypeDefinition type, UserDataBoundTypeDefinition parent = null) public UserDataVariableSymbol(string name, BoundTypeDefinition type, UserDataBoundTypeDefinition parent = null)
@ -20,11 +20,11 @@ namespace Upsilon.Binder.VariableSymbols
Parent = parent; Parent = parent;
} }
private Type _type; private TypeContainer _typeContainer;
public override Type Type public override TypeContainer TypeContainer
{ {
get => BoundTypeDefinition?.ScriptType ?? _type; get => BoundTypeDefinition?.ScriptType ?? _typeContainer;
set => _type = Type; set => _typeContainer = value;
} }
} }
} }

View File

@ -4,14 +4,14 @@ namespace Upsilon.Binder.VariableSymbols
{ {
public class VariableSymbol public class VariableSymbol
{ {
public VariableSymbol(string name, Type type, bool local) public VariableSymbol(string name, TypeContainer typeContainer, bool local)
{ {
Type = type; TypeContainer = typeContainer;
Local = local; Local = local;
Name = name; Name = name;
} }
public virtual Type Type { get; set; } = Type.Unknown; public virtual TypeContainer TypeContainer { get; set; }
public bool Local { get; } public bool Local { get; }
public string Name { get; } public string Name { get; }
public string[] CommentValue { get; set; } public string[] CommentValue { get; set; }

View File

@ -191,7 +191,7 @@ namespace Upsilon.BoundTypes
if (functionParameter.Type.HasFlag(Type.UserData)) if (functionParameter.Type.HasFlag(Type.UserData))
{ {
var variable = Binder.Binder.ResolveVariable(callingParameter, null); var variable = Binder.Binder.ResolveVariable(callingParameter, null);
if (variable != null && variable.Type == Type.UserData) if (variable != null && variable.TypeContainer == Type.UserData)
{ {
var parent = var parent =
(UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition; (UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition;

View File

@ -1,9 +1,9 @@
using System; using System;
using System.Collections.Generic;
using Upsilon.BaseTypes; using Upsilon.BaseTypes;
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;
// ReSharper disable UnusedMember.Global // ReSharper disable UnusedMember.Global
@ -39,19 +39,27 @@ namespace Upsilon.StandardLibraries
} }
[ScriptFunction("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)] directScriptManipulation: true, overrideReturnType: typeof(BasicFunctions), overrideReturnMethod: nameof(PairsBindHandler))]
public IIterable UpTillNullPairs(IIterable table) public UpTillNullPairsScriptIterator UpTillNullPairs(IIterable table)
{ {
return new UpTillNullPairsScriptIterator(table); return new UpTillNullPairsScriptIterator(table);
} }
[ScriptFunction("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)] directScriptManipulation: true, overrideReturnType: typeof(BasicFunctions), overrideReturnMethod: nameof(PairsBindHandler))]
public IIterable Pairs(IIterable table) public PairsScriptIterator Pairs(IIterable table)
{ {
return new PairsScriptIterator(table); return new PairsScriptIterator(table);
} }
private static TypeContainer PairsBindHandler(BoundExpression[] variableSymbols)
{
if (variableSymbols.Length != 1)
return BaseTypes.Type.Unknown;
var parameter = variableSymbols[0];
return parameter.Type;
}
[ScriptFunction("print", "Prints a message to the action given in the script options", passScriptReference: true, [ScriptFunction("print", "Prints a message to the action given in the script options", passScriptReference: true,
directScriptManipulation: true)] directScriptManipulation: true)]
public void Print(Script script, ScriptType message) public void Print(Script script, ScriptType message)

View File

@ -1,4 +1,5 @@
using System; using System;
using System.Reflection;
namespace Upsilon.StandardLibraries namespace Upsilon.StandardLibraries
{ {
@ -6,13 +7,20 @@ namespace Upsilon.StandardLibraries
public class ScriptFunctionAttribute : Attribute public class ScriptFunctionAttribute : Attribute
{ {
public ScriptFunctionAttribute(string name, string comment = null, public ScriptFunctionAttribute(string name, string comment = null,
bool directScriptManipulation = false, bool passScriptReference = false, bool passScopeReference = false) bool directScriptManipulation = false, bool passScriptReference = false, bool passScopeReference = false,
Type overrideReturnType = null, string overrideReturnMethod = null)
{ {
Name = name; Name = name;
Comment = comment; Comment = comment;
DirectScriptManipulation = directScriptManipulation; DirectScriptManipulation = directScriptManipulation;
PassScriptReference = passScriptReference; PassScriptReference = passScriptReference;
PassScopeReference = passScopeReference; PassScopeReference = passScopeReference;
if (overrideReturnType != null && overrideReturnMethod != null)
{
var method = overrideReturnType.GetMethod(overrideReturnMethod,
BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Static);
OverrideReturnType = method;
}
} }
public string Name { get; } public string Name { get; }
@ -20,5 +28,6 @@ namespace Upsilon.StandardLibraries
public bool DirectScriptManipulation { get; } public bool DirectScriptManipulation { get; }
public bool PassScriptReference { get; } public bool PassScriptReference { get; }
public bool PassScopeReference { get; } public bool PassScopeReference { get; }
public MethodInfo OverrideReturnType { get; }
} }
} }

View File

@ -65,7 +65,7 @@ namespace Upsilon.StandardLibraries
var derivedType = DeriveValidTypes(typeInfo.Type); var derivedType = DeriveValidTypes(typeInfo.Type);
return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType, return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType,
typeInfo.IsOptional); typeInfo.IsOptional);
}).ToArray()) }).ToArray(), func.Value.MethodInfoFunction.Method.GetMethods()[0].Attribute.OverrideReturnType)
{ {
CommentValue = func.Value.CommentValue?.Split('\n') CommentValue = func.Value.CommentValue?.Split('\n')
}; };
@ -128,7 +128,7 @@ namespace Upsilon.StandardLibraries
} }
var result = genericParameters[genericParameters.Length - 1].GetScriptType(); var result = genericParameters[genericParameters.Length - 1].GetScriptType();
return new InternalFunctionVariableSymbol(name, true, result, parameters.ToArray()); return new InternalFunctionVariableSymbol(name, true, result, parameters.ToArray(), null);
} }
private static VariableSymbol BuildActionVariableSymbol(string name, System.Type type) private static VariableSymbol BuildActionVariableSymbol(string name, System.Type type)
@ -136,10 +136,10 @@ namespace Upsilon.StandardLibraries
var genericParameters = type.GetGenericArguments(); var genericParameters = type.GetGenericArguments();
return new InternalFunctionVariableSymbol(name, true, Type.Nil, return new InternalFunctionVariableSymbol(name, true, Type.Nil,
genericParameters.Select(DeriveValidTypes).Select(t => genericParameters.Select(DeriveValidTypes).Select(t =>
new InternalFunctionVariableSymbol.InternalFunctionParameter(name, t, false)).ToArray()); new InternalFunctionVariableSymbol.InternalFunctionParameter(name, t, false)).ToArray(), null);
} }
public static Type DeriveValidTypes(System.Type type) public static TypeContainer DeriveValidTypes(System.Type type)
{ {
if (type.IsEnum) if (type.IsEnum)
return Type.UserData | Type.Number; return Type.UserData | Type.Number;

View File

@ -29,9 +29,7 @@ namespace Upsilon.Text
{ {
if (StartLine == EndLine && linePosition == StartLine) if (StartLine == EndLine && linePosition == StartLine)
{ {
if (characterPosition >= StartPosition && characterPosition <= EndPosition) return characterPosition >= StartPosition && characterPosition <= EndPosition;
return true;
return false;
} }
if (StartLine == linePosition && StartPosition <= characterPosition) return true; if (StartLine == linePosition && StartPosition <= characterPosition) return true;
if (StartLine < linePosition && EndLine > linePosition) return true; if (StartLine < linePosition && EndLine > linePosition) return true;

View File

@ -1,4 +1,5 @@
using Upsilon; using Upsilon;
using Upsilon.BaseTypes;
using Upsilon.Evaluator; using Upsilon.Evaluator;
using Xunit; using Xunit;
@ -98,5 +99,21 @@ return value
var actual = Executor.EvaluateScript<long>(input, Options); var actual = Executor.EvaluateScript<long>(input, Options);
Assert.Equal(6, actual); Assert.Equal(6, actual);
} }
[Fact]
public void GenericForLoopValueTypeBindTest()
{
const string input = @"
arr = {100, 56, 28}
for key, val in ipairs(arr) do
a = val
break
end
";
var script = Executor.ParseInputAndEvaluate(input, Options);
Assert.True(script.Bind().Scope.TryGetVariable("a", true, out var variable));
Assert.Equal(Type.Number, variable.TypeContainer.Type);
}
} }
} }