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

View File

@ -17,5 +17,6 @@ namespace Upsilon.Binder
BoundIfStatement, BoundIfStatement,
BoundElseStatement, BoundElseStatement,
BoundFunctionStatement, 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 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)

View File

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

View File

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