From da6e95bfac91391fb009b5478dfba21cf1345ca8 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Thu, 15 Nov 2018 20:48:52 +0100 Subject: [PATCH] Resolve function parameter types by checking calls to the function --- Upsilon/Binder/Binder.cs | 38 +++++++++++++++---- Upsilon/Binder/BoundKind.cs | 1 + .../UnboundFunctionStatement.cs | 23 +++++++++++ Upsilon/Binder/UnboundFunction.cs | 7 ---- Upsilon/Binder/VariableSymbol.cs | 1 + Upsilon/Evaluator/Evaluator.cs | 15 ++++++++ UpsilonTests/FunctionTests.cs | 21 +++++++++- 7 files changed, 89 insertions(+), 17 deletions(-) create mode 100644 Upsilon/Binder/BoundStatements/UnboundFunctionStatement.cs delete mode 100644 Upsilon/Binder/UnboundFunction.cs diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 1a1c762..b0856da 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -13,6 +13,9 @@ namespace Upsilon.Binder private readonly Diagnostics _diagnostics; private BoundScope _scope; + private Dictionary _unboundFunctions = + new Dictionary(); + 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; + } } } diff --git a/Upsilon/Binder/BoundKind.cs b/Upsilon/Binder/BoundKind.cs index de868a9..efbfc1f 100644 --- a/Upsilon/Binder/BoundKind.cs +++ b/Upsilon/Binder/BoundKind.cs @@ -17,5 +17,6 @@ namespace Upsilon.Binder BoundIfStatement, BoundElseStatement, BoundFunctionStatement, + BoundPromise } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/UnboundFunctionStatement.cs b/Upsilon/Binder/BoundStatements/UnboundFunctionStatement.cs new file mode 100644 index 0000000..8c335e8 --- /dev/null +++ b/Upsilon/Binder/BoundStatements/UnboundFunctionStatement.cs @@ -0,0 +1,23 @@ +using System.Collections.Immutable; +using Upsilon.Parser; + +namespace Upsilon.Binder +{ + public class UnboundFunctionStatement : BoundStatement + { + public UnboundFunctionStatement(VariableSymbol identifier, ImmutableArray parameters, + BlockStatementSyntax unboundBlock) + { + Identifier = identifier; + Parameters = parameters; + UnboundBlock = unboundBlock; + } + + public override BoundKind Kind => BoundKind.BoundPromise; + + public VariableSymbol Identifier { get; } + public ImmutableArray Parameters { get; } + public BlockStatementSyntax UnboundBlock { get; } + public BoundBlockStatement Block { get; set; } + } +} \ No newline at end of file diff --git a/Upsilon/Binder/UnboundFunction.cs b/Upsilon/Binder/UnboundFunction.cs deleted file mode 100644 index 2e8cfa8..0000000 --- a/Upsilon/Binder/UnboundFunction.cs +++ /dev/null @@ -1,7 +0,0 @@ -namespace Upsilon.Binder -{ - public class UnboundFunction - { - - } -} \ No newline at end of file diff --git a/Upsilon/Binder/VariableSymbol.cs b/Upsilon/Binder/VariableSymbol.cs index 6f50403..947c4a3 100644 --- a/Upsilon/Binder/VariableSymbol.cs +++ b/Upsilon/Binder/VariableSymbol.cs @@ -20,6 +20,7 @@ namespace Upsilon.Binder public class FunctionVariableSymbol : VariableSymbol { public ImmutableArray Parameters { get; } + public bool IsBound { get; set; } public FunctionVariableSymbol(string name, Type type, bool local, ImmutableArray parameters) : base(name, type, local) diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 80e5938..65f9d99 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -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; } + + } } \ No newline at end of file diff --git a/UpsilonTests/FunctionTests.cs b/UpsilonTests/FunctionTests.cs index 6b82787..398d322 100644 --- a/UpsilonTests/FunctionTests.cs +++ b/UpsilonTests/FunctionTests.cs @@ -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(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(func); + Assert.NotNull(castType.Block); + } + } } \ No newline at end of file