Rework type binding to allow for type awareness in iterators
This commit is contained in:
parent
64aedceb85
commit
f55e6d314d
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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,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());
|
||||
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);
|
||||
|
|
|
@ -17,6 +17,6 @@ namespace Upsilon.Binder
|
|||
yield break;
|
||||
}
|
||||
|
||||
public override Type Type => Type.Nil;
|
||||
public override TypeContainer Type => BaseTypes.Type.Nil;
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -9,6 +9,6 @@ namespace Upsilon.Binder
|
|||
{
|
||||
}
|
||||
|
||||
public abstract Type Type { get; }
|
||||
public abstract TypeContainer Type { get; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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)
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,6 @@ namespace Upsilon.Binder
|
|||
yield break;
|
||||
}
|
||||
|
||||
public override Type Type => VariableSymbol.Type;
|
||||
public override TypeContainer Type => VariableSymbol.TypeContainer;
|
||||
}
|
||||
}
|
|
@ -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{}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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; }
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue