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,
bool passScriptReference = false, bool passScopeReference = false)
{
_method = method;
Method = method;
_object = o;
_directTypeManipulation = directTypeManipulation;
_passScriptReference = passScriptReference;
_passScopeReference = passScopeReference;
ReturnType = _method.ReturnType;
ReturnType = Method.ReturnType;
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 bool _directTypeManipulation;
private readonly bool _passScriptReference;
@ -42,7 +42,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
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)
@ -61,7 +61,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
{
// grab the parameters, and just invoke it
var array = objects.ToArray();
var methodInfo = _method.GetMethod(ref array);
var methodInfo = Method.GetMethod(ref array);
result = methodInfo.Invoke(null, array);
}
else
@ -90,7 +90,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
foreach (var match in methods)
{
// 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
if (UpsilonBinder.Default.IsValidMatch(match, convertedTypes))
{
@ -108,7 +108,7 @@ namespace Upsilon.BaseTypes.ScriptFunction
if (method == null)
{
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));
}
// 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)
{
Scope.AssignToNearest(valueParameter.VariableSymbol);
if (valueParameter.VariableSymbol.Type == Type.Unknown)
if (valueParameter.VariableSymbol.TypeContainer == Type.Unknown)
_diagnostics.LogUnknownVariableType(valueParameter.VariableSymbol.Name, valueParameter.Span);
}
unboundFunctionStatement.Value.Block =
@ -220,13 +220,14 @@ namespace Upsilon.Binder
parameters.Add(bound);
}
var returnType = Type.Unknown;
TypeContainer returnType = Type.Unknown;
var resolved = ResolveVariable(expression, _diagnostics);
if (resolved == null)
{
_diagnostics.LogError("Can't resolve variable", expression.Span);
}
if (resolved is FunctionVariableSymbol function)
else if (resolved is FunctionVariableSymbol function)
{
if (function is ScriptFunctionVariableSymbol scriptFunction)
{
@ -237,7 +238,7 @@ namespace Upsilon.Binder
{
var functionVariable = scriptFunction.Parameters[index];
var callingVariable = parameters[index];
functionVariable.Type = callingVariable.Type;
functionVariable.TypeContainer = callingVariable.Type;
Scope.DefineLocalVariable(functionVariable);
}
@ -280,9 +281,15 @@ namespace Upsilon.Binder
}
}
}
if (resolved is InternalFunctionVariableSymbol internalFunction)
{
returnType = internalFunction.GetResultType(parameters.ToArray());
}
else
{
returnType = function.ResultType;
}
}
var (isValid, error, wrongParameter) = function.ValidateParameters(parameters.ToImmutable());
if (!isValid)
@ -331,11 +338,11 @@ namespace Upsilon.Binder
{
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];
}
else if (indexerVariable.Type == Type.UserData)
else if (indexerVariable.TypeContainer == Type.UserData)
{
var parent =
(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)
{
@ -405,17 +412,18 @@ namespace Upsilon.Binder
{
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)
{
variable = new TableVariableSymbol(name, isLocal,
((TableVariableSymbol) ((BoundVariableExpression) assignment).Variable.VariableSymbol)
.Variables);
.Variables, assignment.Type);
}
else
{
variable = new TableVariableSymbol(name, isLocal);
variable = new TableVariableSymbol(name, isLocal, assignment.Type);
}
}
else
@ -435,26 +443,26 @@ namespace Upsilon.Binder
else
{
// 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)
{
v.Variable.VariableSymbol.Type = variable.Type;
v.Variable.VariableSymbol.TypeContainer = variable.TypeContainer;
}
else if (assignment.Type == Type.Unknown)
{
}
else
{
_diagnostics.LogCannotConvert(assignment.Type, variable.Type, assignment.Span);
_diagnostics.LogCannotConvert(assignment.Type, variable.TypeContainer, assignment.Span);
return (null, false);
}
}
@ -474,7 +482,7 @@ namespace Upsilon.Binder
var (symbol, isCreation) = TryBindVariable(name, isLocal, boundExpression, e.CommentData);
if (symbol != null)
{
if (symbol.Type == Type.Unknown)
if (symbol.TypeContainer == Type.Unknown)
{
_diagnostics.LogUnknownVariableType(symbol.Name, variableExpression.Span);
}
@ -496,7 +504,7 @@ namespace Upsilon.Binder
foreach (var identifierToken in s.Identifiers)
{
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);
}
@ -610,7 +618,7 @@ namespace Upsilon.Binder
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);
innerScope.DefineLocalVariable(vari);
}
@ -637,15 +645,15 @@ namespace Upsilon.Binder
else
{
// 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
{
_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);
}
}
@ -712,7 +720,7 @@ namespace Upsilon.Binder
_diagnostics.LogInvalidIndexExpression(expression.Type, index.Type, e.Span);
return new BoundBadExpression(e.Span);
}
switch (expression.Type)
switch (expression.Type.Type)
{
case Type.Table:
if (isAssignment)
@ -726,7 +734,7 @@ namespace Upsilon.Binder
var variableDic = table.Expressions;
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.",
@ -746,7 +754,7 @@ namespace Upsilon.Binder
var variableDic = tableSymbol.Variables;
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}'.",
@ -768,7 +776,7 @@ namespace Upsilon.Binder
{
var expression = BindExpression(e.Expression);
var index = e.Index.Name;
switch (expression.Type)
switch (expression.Type.Type)
{
case Type.Table:
if (isAssignment)
@ -780,7 +788,7 @@ namespace Upsilon.Binder
var table = (BoundTableExpression)expression;
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);
}
@ -791,7 +799,7 @@ namespace Upsilon.Binder
var variableDic = ((TableVariableSymbol) realTable.VariableSymbol).Variables;
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}'.",
@ -897,13 +905,29 @@ namespace Upsilon.Binder
{
Scope = new BoundScope(Scope);
var array = ImmutableArray.CreateBuilder<BoundVariableSymbol>();
foreach (var variableIdentifier in e.Variables)
{
var variable = new VariableSymbol(variableIdentifier.Name, Type.Unknown, true);
Scope.DefineLocalVariable(variable);
array.Add(new BoundVariableSymbol(variable, true, variableIdentifier.Span));
}
var keyVar = e.Variables[0];
var keyVariable = new VariableSymbol(keyVar.Name, Type.String, true);
Scope.DefineLocalVariable(keyVariable);
array.Add(new BoundVariableSymbol(keyVariable, true, keyVar.Span));
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);
return new BoundGenericForStatement(array.ToImmutable(), boundEnumerableExpression, block, e.Span);

View File

@ -17,6 +17,6 @@ namespace Upsilon.Binder
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;
}
public override Type Type { get; }
public override TypeContainer Type { get; }
public BoundBinaryOperator Operator { 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 BoundFunctionCallExpression(BoundExpression identifier, ImmutableArray<BoundExpression> parameters,
TextSpan span, Type type) : base(span)
TextSpan span, TypeContainer type) : base(span)
{
Identifier = identifier;
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.Collections.Generic;
using System.Collections.Immutable;
using Upsilon.BaseTypes;
using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type;
@ -31,7 +32,7 @@ namespace Upsilon.Binder
yield return Block;
}
public override Type Type => Type.Function;
public override TypeContainer Type => BaseTypes.Type.Function;
public BoundScope Scope { get; set; }
public Type ReturnType { get; }
}

View File

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

View File

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

View File

@ -1,6 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using Upsilon.BaseTypes;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type;
@ -24,7 +24,39 @@ namespace Upsilon.Binder
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; }

View File

@ -13,7 +13,7 @@ namespace Upsilon.Binder
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)
{

View File

@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using Upsilon.BaseTypes;
using Upsilon.Text;
using Type = Upsilon.BaseTypes.Type;
@ -20,6 +21,6 @@ namespace Upsilon.Binder
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,
BlockStatementSyntax unboundBlock, TextSpan span, BoundScope scope)
: base(parameters, null, span, scope, Type.Unknown)
: base(parameters, null, span, scope, BaseTypes.Type.Unknown)
{
UnboundBlock = unboundBlock;
}

View File

@ -27,6 +27,6 @@ namespace Upsilon.Binder
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 FunctionVariableSymbol(string name, bool local, Type resultType)
: base(name, Type.Function, local)
: base(name, BaseTypes.Type.Function, local)
{
ResultType = resultType;
}
public abstract (bool IsValid, string Error, BoundExpression WrongParameter) ValidateParameters(
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.Linq;
using System.Reflection;
using Upsilon.BaseTypes;
using Upsilon.BoundTypes;
using Type = Upsilon.BaseTypes.Type;
@ -8,37 +9,17 @@ namespace Upsilon.Binder.VariableSymbols
{
public class InternalFunctionVariableSymbol : FunctionVariableSymbol
{
public class InternalFunctionParameter
{
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 InternalFunctionParameter[] FunctionParameters { 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)
{
FunctionParameters = functionParameters;
MinimalParametersRequired = functionParameters.Count(x => !x.IsOptional);
OverrideResultType = overrideResultType;
}
public override (bool IsValid, string Error,
@ -70,7 +51,7 @@ namespace Upsilon.Binder.VariableSymbols
if (functionParameter.ValidTypes.HasFlag(Type.UserData))
{
var variable = Binder.ResolveVariable(callingParameter, null);
if (variable != null && variable.Type == Type.UserData)
if (variable != null && variable.TypeContainer == Type.UserData)
{
var parent =
(UserDataBoundTypeDefinition) ((UserDataVariableSymbol) variable).BoundTypeDefinition;
@ -87,5 +68,35 @@ namespace Upsilon.Binder.VariableSymbols
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 callingParameter = callingParameters[i];
if (functionParameter.Type != Type.Unknown &&
callingParameter.Type != Type.Unknown && callingParameter.Type != Type.Nil)
if (functionParameter.TypeContainer != BaseTypes.Type.Unknown &&
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}'. " +
$"Expected type '{functionParameter.Type}', got '{callingParameter.Type}'",
$"Expected type '{functionParameter.TypeContainer}', got '{callingParameter.Type}'",
callingParameter);
}
}

View File

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

View File

@ -8,9 +8,9 @@ namespace Upsilon.Binder.VariableSymbols
public BoundTypeDefinition BoundTypeDefinition { 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)
@ -20,11 +20,11 @@ namespace Upsilon.Binder.VariableSymbols
Parent = parent;
}
private Type _type;
public override Type Type
private TypeContainer _typeContainer;
public override TypeContainer TypeContainer
{
get => BoundTypeDefinition?.ScriptType ?? _type;
set => _type = Type;
get => BoundTypeDefinition?.ScriptType ?? _typeContainer;
set => _typeContainer = value;
}
}
}

View File

@ -4,14 +4,14 @@ namespace Upsilon.Binder.VariableSymbols
{
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;
Name = name;
}
public virtual Type Type { get; set; } = Type.Unknown;
public virtual TypeContainer TypeContainer { get; set; }
public bool Local { get; }
public string Name { get; }
public string[] CommentValue { get; set; }

View File

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

View File

@ -1,9 +1,9 @@
using System;
using System.Collections.Generic;
using Upsilon.BaseTypes;
using Upsilon.BaseTypes.Number;
using Upsilon.BaseTypes.ScriptTypeInterfaces;
using Upsilon.Binder;
using Upsilon.Binder.VariableSymbols;
using Upsilon.Evaluator;
// 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.",
directScriptManipulation: true)]
public IIterable UpTillNullPairs(IIterable table)
directScriptManipulation: true, overrideReturnType: typeof(BasicFunctions), overrideReturnMethod: nameof(PairsBindHandler))]
public UpTillNullPairsScriptIterator UpTillNullPairs(IIterable table)
{
return new UpTillNullPairsScriptIterator(table);
}
[ScriptFunction("pairs", "Iterates over an iterable variable, like a table, skipping all nil values.",
directScriptManipulation: true)]
public IIterable Pairs(IIterable table)
directScriptManipulation: true, overrideReturnType: typeof(BasicFunctions), overrideReturnMethod: nameof(PairsBindHandler))]
public PairsScriptIterator Pairs(IIterable 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,
directScriptManipulation: true)]
public void Print(Script script, ScriptType message)

View File

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

View File

@ -65,7 +65,7 @@ namespace Upsilon.StandardLibraries
var derivedType = DeriveValidTypes(typeInfo.Type);
return new InternalFunctionVariableSymbol.InternalFunctionParameter(func.Key, derivedType,
typeInfo.IsOptional);
}).ToArray())
}).ToArray(), func.Value.MethodInfoFunction.Method.GetMethods()[0].Attribute.OverrideReturnType)
{
CommentValue = func.Value.CommentValue?.Split('\n')
};
@ -128,7 +128,7 @@ namespace Upsilon.StandardLibraries
}
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)
@ -136,10 +136,10 @@ namespace Upsilon.StandardLibraries
var genericParameters = type.GetGenericArguments();
return new InternalFunctionVariableSymbol(name, true, Type.Nil,
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)
return Type.UserData | Type.Number;

View File

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

View File

@ -1,4 +1,5 @@
using Upsilon;
using Upsilon.BaseTypes;
using Upsilon.Evaluator;
using Xunit;
@ -98,5 +99,21 @@ return value
var actual = Executor.EvaluateScript<long>(input, Options);
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);
}
}
}