Resolve function parameter types by checking calls to the function

This commit is contained in:
Deukhoofd 2018-11-15 20:48:52 +01:00
parent d5c8a959fb
commit da6e95bfac
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
7 changed files with 89 additions and 17 deletions

View File

@ -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);
}
for (var index = 0; index < function.Parameters.Length; index++)
if (!function.IsBound)
{
var parameter = function.Parameters[index];
if (parameter.Type == Type.Unknown)
_scope = new BoundScope(_scope);
for (var index = 0; index < function.Parameters.Length; index++)
{
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,10 +309,20 @@ namespace Upsilon.Binder
}
}
_scope = innerScope;
var block = BindBlockStatement(e.Block);
_scope = _scope.ParentScope;
return new BoundFunctionStatement(variable, parameters.ToImmutable(), (BoundBlockStatement) block);
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;
}
}
}

View File

@ -17,5 +17,6 @@ namespace Upsilon.Binder
BoundIfStatement,
BoundElseStatement,
BoundFunctionStatement,
BoundPromise
}
}

View File

@ -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; }
}
}

View File

@ -1,7 +0,0 @@
namespace Upsilon.Binder
{
public class UnboundFunction
{
}
}

View File

@ -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)

View File

@ -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;
}
}
}

View File

@ -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);
}
}
}