Resolve function parameter types by checking calls to the function
This commit is contained in:
parent
d5c8a959fb
commit
da6e95bfac
|
@ -13,6 +13,9 @@ namespace Upsilon.Binder
|
|||
private readonly Diagnostics _diagnostics;
|
||||
private BoundScope _scope;
|
||||
|
||||
private Dictionary<FunctionVariableSymbol, UnboundFunctionStatement> _unboundFunctions =
|
||||
new Dictionary<FunctionVariableSymbol, UnboundFunctionStatement>();
|
||||
|
||||
public Binder(Diagnostics diagnostics)
|
||||
{
|
||||
_diagnostics = diagnostics;
|
||||
|
@ -144,13 +147,22 @@ namespace Upsilon.Binder
|
|||
parameters.Add(bound);
|
||||
}
|
||||
|
||||
if (!function.IsBound)
|
||||
{
|
||||
_scope = new BoundScope(_scope);
|
||||
for (var index = 0; index < function.Parameters.Length; index++)
|
||||
{
|
||||
var parameter = function.Parameters[index];
|
||||
if (parameter.Type == Type.Unknown)
|
||||
{
|
||||
parameter.Type = parameters[index].Type;
|
||||
var functionVariable = function.Parameters[index];
|
||||
var callingVariable = parameters[index];
|
||||
functionVariable.Type = callingVariable.Type;
|
||||
_scope.SetVariable(functionVariable);
|
||||
}
|
||||
|
||||
_unboundFunctions[function].Block =
|
||||
(BoundBlockStatement) BindBlockStatement(_unboundFunctions[function].UnboundBlock);
|
||||
_scope = _scope.ParentScope;
|
||||
function.IsBound = true;
|
||||
_unboundFunctions.Remove(function);
|
||||
}
|
||||
|
||||
//TODO: validate parameters
|
||||
|
@ -297,11 +309,21 @@ namespace Upsilon.Binder
|
|||
}
|
||||
}
|
||||
|
||||
if (parameters.Count == 0)
|
||||
{
|
||||
_scope = innerScope;
|
||||
var block = BindBlockStatement(e.Block);
|
||||
_scope = _scope.ParentScope;
|
||||
((FunctionVariableSymbol) variable).IsBound = true;
|
||||
return new BoundFunctionStatement(variable, parameters.ToImmutable(), (BoundBlockStatement) block);
|
||||
}
|
||||
else
|
||||
{
|
||||
var unbound = new UnboundFunctionStatement(variable, parameters.ToImmutable(), e.Block);
|
||||
_unboundFunctions.Add((FunctionVariableSymbol) variable, unbound);
|
||||
return unbound;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
|
@ -17,5 +17,6 @@ namespace Upsilon.Binder
|
|||
BoundIfStatement,
|
||||
BoundElseStatement,
|
||||
BoundFunctionStatement,
|
||||
BoundPromise
|
||||
}
|
||||
}
|
|
@ -0,0 +1,23 @@
|
|||
using System.Collections.Immutable;
|
||||
using Upsilon.Parser;
|
||||
|
||||
namespace Upsilon.Binder
|
||||
{
|
||||
public class UnboundFunctionStatement : BoundStatement
|
||||
{
|
||||
public UnboundFunctionStatement(VariableSymbol identifier, ImmutableArray<VariableSymbol> parameters,
|
||||
BlockStatementSyntax unboundBlock)
|
||||
{
|
||||
Identifier = identifier;
|
||||
Parameters = parameters;
|
||||
UnboundBlock = unboundBlock;
|
||||
}
|
||||
|
||||
public override BoundKind Kind => BoundKind.BoundPromise;
|
||||
|
||||
public VariableSymbol Identifier { get; }
|
||||
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||
public BlockStatementSyntax UnboundBlock { get; }
|
||||
public BoundBlockStatement Block { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,7 +0,0 @@
|
|||
namespace Upsilon.Binder
|
||||
{
|
||||
public class UnboundFunction
|
||||
{
|
||||
|
||||
}
|
||||
}
|
|
@ -20,6 +20,7 @@ namespace Upsilon.Binder
|
|||
public class FunctionVariableSymbol : VariableSymbol
|
||||
{
|
||||
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||
public bool IsBound { get; set; }
|
||||
|
||||
public FunctionVariableSymbol(string name, Type type, bool local, ImmutableArray<VariableSymbol> parameters)
|
||||
: base(name, type, local)
|
||||
|
|
|
@ -40,6 +40,9 @@ namespace Upsilon.Evaluator
|
|||
case BoundKind.BoundFunctionStatement:
|
||||
EvaluateBoundFunctionStatement((BoundFunctionStatement) e);
|
||||
break;
|
||||
case BoundKind.BoundPromise:
|
||||
EvaluateUnboundFunctionStatement((UnboundFunctionStatement) e);
|
||||
break;
|
||||
default:
|
||||
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
||||
break;
|
||||
|
@ -170,6 +173,16 @@ namespace Upsilon.Evaluator
|
|||
Scope.SetGlobal(boundFunctionStatement.Identifier, func);
|
||||
}
|
||||
|
||||
private void EvaluateUnboundFunctionStatement(UnboundFunctionStatement unboundFunctionStatement)
|
||||
{
|
||||
var func = new LuaFunction(unboundFunctionStatement.Parameters, unboundFunctionStatement.Block);
|
||||
if (unboundFunctionStatement.Identifier.Local)
|
||||
Scope.Set(unboundFunctionStatement.Identifier, func);
|
||||
else
|
||||
Scope.SetGlobal(unboundFunctionStatement.Identifier, func);
|
||||
|
||||
}
|
||||
|
||||
private LuaType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression)
|
||||
{
|
||||
var name = boundFunctionCallExpression.Identifier;
|
||||
|
@ -191,5 +204,7 @@ namespace Upsilon.Evaluator
|
|||
Scope = Scope.ParentScope;
|
||||
return _value;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
|
@ -52,17 +52,34 @@ testFunc(100)
|
|||
{
|
||||
const string input = @"
|
||||
function testFunc (var1)
|
||||
b = var1 == true
|
||||
var1 == true
|
||||
end
|
||||
testFunc(100)
|
||||
";
|
||||
var script = new Script(input);
|
||||
Assert.Empty(script.Diagnostics.Messages);
|
||||
script.Evaluate();
|
||||
Assert.Empty(script.Diagnostics.Messages);
|
||||
Assert.Single(script.Diagnostics.Messages);
|
||||
Assert.True(script.Scope.TryGet("testFunc", out var func));
|
||||
Assert.IsType<LuaFunction>(func);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public void BindUnusedFunctions()
|
||||
{
|
||||
const string input = @"
|
||||
function testFunc (var1)
|
||||
var1 == true
|
||||
end
|
||||
";
|
||||
var script = new Script(input);
|
||||
Assert.Empty(script.Diagnostics.Messages);
|
||||
script.Evaluate();
|
||||
Assert.Empty(script.Diagnostics.Messages);
|
||||
Assert.True(script.Scope.TryGet("testFunc", out var func));
|
||||
var castType = Assert.IsType<LuaFunction>(func);
|
||||
Assert.NotNull(castType.Block);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue