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 readonly Diagnostics _diagnostics;
|
||||||
private BoundScope _scope;
|
private BoundScope _scope;
|
||||||
|
|
||||||
|
private Dictionary<FunctionVariableSymbol, UnboundFunctionStatement> _unboundFunctions =
|
||||||
|
new Dictionary<FunctionVariableSymbol, UnboundFunctionStatement>();
|
||||||
|
|
||||||
public Binder(Diagnostics diagnostics)
|
public Binder(Diagnostics diagnostics)
|
||||||
{
|
{
|
||||||
_diagnostics = diagnostics;
|
_diagnostics = diagnostics;
|
||||||
|
@ -144,13 +147,22 @@ namespace Upsilon.Binder
|
||||||
parameters.Add(bound);
|
parameters.Add(bound);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!function.IsBound)
|
||||||
|
{
|
||||||
|
_scope = new BoundScope(_scope);
|
||||||
for (var index = 0; index < function.Parameters.Length; index++)
|
for (var index = 0; index < function.Parameters.Length; index++)
|
||||||
{
|
{
|
||||||
var parameter = function.Parameters[index];
|
var functionVariable = function.Parameters[index];
|
||||||
if (parameter.Type == Type.Unknown)
|
var callingVariable = parameters[index];
|
||||||
{
|
functionVariable.Type = callingVariable.Type;
|
||||||
parameter.Type = parameters[index].Type;
|
_scope.SetVariable(functionVariable);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_unboundFunctions[function].Block =
|
||||||
|
(BoundBlockStatement) BindBlockStatement(_unboundFunctions[function].UnboundBlock);
|
||||||
|
_scope = _scope.ParentScope;
|
||||||
|
function.IsBound = true;
|
||||||
|
_unboundFunctions.Remove(function);
|
||||||
}
|
}
|
||||||
|
|
||||||
//TODO: validate parameters
|
//TODO: validate parameters
|
||||||
|
@ -297,11 +309,21 @@ namespace Upsilon.Binder
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (parameters.Count == 0)
|
||||||
|
{
|
||||||
_scope = innerScope;
|
_scope = innerScope;
|
||||||
var block = BindBlockStatement(e.Block);
|
var block = BindBlockStatement(e.Block);
|
||||||
_scope = _scope.ParentScope;
|
_scope = _scope.ParentScope;
|
||||||
|
((FunctionVariableSymbol) variable).IsBound = true;
|
||||||
return new BoundFunctionStatement(variable, parameters.ToImmutable(), (BoundBlockStatement) block);
|
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,
|
BoundIfStatement,
|
||||||
BoundElseStatement,
|
BoundElseStatement,
|
||||||
BoundFunctionStatement,
|
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 class FunctionVariableSymbol : VariableSymbol
|
||||||
{
|
{
|
||||||
public ImmutableArray<VariableSymbol> Parameters { get; }
|
public ImmutableArray<VariableSymbol> Parameters { get; }
|
||||||
|
public bool IsBound { get; set; }
|
||||||
|
|
||||||
public FunctionVariableSymbol(string name, Type type, bool local, ImmutableArray<VariableSymbol> parameters)
|
public FunctionVariableSymbol(string name, Type type, bool local, ImmutableArray<VariableSymbol> parameters)
|
||||||
: base(name, type, local)
|
: base(name, type, local)
|
||||||
|
|
|
@ -40,6 +40,9 @@ namespace Upsilon.Evaluator
|
||||||
case BoundKind.BoundFunctionStatement:
|
case BoundKind.BoundFunctionStatement:
|
||||||
EvaluateBoundFunctionStatement((BoundFunctionStatement) e);
|
EvaluateBoundFunctionStatement((BoundFunctionStatement) e);
|
||||||
break;
|
break;
|
||||||
|
case BoundKind.BoundPromise:
|
||||||
|
EvaluateUnboundFunctionStatement((UnboundFunctionStatement) e);
|
||||||
|
break;
|
||||||
default:
|
default:
|
||||||
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
EvaluateExpressionStatement((BoundExpressionStatement) e);
|
||||||
break;
|
break;
|
||||||
|
@ -170,6 +173,16 @@ namespace Upsilon.Evaluator
|
||||||
Scope.SetGlobal(boundFunctionStatement.Identifier, func);
|
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)
|
private LuaType EvaluateBoundFunctionCallExpression(BoundFunctionCallExpression boundFunctionCallExpression)
|
||||||
{
|
{
|
||||||
var name = boundFunctionCallExpression.Identifier;
|
var name = boundFunctionCallExpression.Identifier;
|
||||||
|
@ -191,5 +204,7 @@ namespace Upsilon.Evaluator
|
||||||
Scope = Scope.ParentScope;
|
Scope = Scope.ParentScope;
|
||||||
return _value;
|
return _value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -52,17 +52,34 @@ testFunc(100)
|
||||||
{
|
{
|
||||||
const string input = @"
|
const string input = @"
|
||||||
function testFunc (var1)
|
function testFunc (var1)
|
||||||
b = var1 == true
|
var1 == true
|
||||||
end
|
end
|
||||||
testFunc(100)
|
testFunc(100)
|
||||||
";
|
";
|
||||||
var script = new Script(input);
|
var script = new Script(input);
|
||||||
Assert.Empty(script.Diagnostics.Messages);
|
Assert.Empty(script.Diagnostics.Messages);
|
||||||
script.Evaluate();
|
script.Evaluate();
|
||||||
Assert.Empty(script.Diagnostics.Messages);
|
Assert.Single(script.Diagnostics.Messages);
|
||||||
Assert.True(script.Scope.TryGet("testFunc", out var func));
|
Assert.True(script.Scope.TryGet("testFunc", out var func));
|
||||||
Assert.IsType<LuaFunction>(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