From 2db4d0607ef6b3436f7890b1701cbf69f33fb0d1 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Fri, 23 Nov 2018 15:27:48 +0100 Subject: [PATCH] Implements break statement --- Upsilon/Binder/Binder.cs | 2 ++ Upsilon/Binder/BoundKind.cs | 5 ++-- .../BoundStatements/BoundBreakStatement.cs | 7 ++++++ Upsilon/Evaluator/Evaluator.cs | 23 +++++++++++-------- Upsilon/Parser/Parser.cs | 4 ++++ .../StatementSyntax/BreakStatementSyntax.cs | 19 +++++++++++++++ Upsilon/Parser/SyntaxKeyWords.cs | 2 ++ Upsilon/Parser/SyntaxKind.cs | 4 +++- UpsilonTests/GeneralTests/ForLoopTests.cs | 20 ++++++++++++++++ UpsilonTests/GeneralTests/ScopeTests.cs | 3 ++- 10 files changed, 76 insertions(+), 13 deletions(-) create mode 100644 Upsilon/Binder/BoundStatements/BoundBreakStatement.cs create mode 100644 Upsilon/Parser/StatementSyntax/BreakStatementSyntax.cs diff --git a/Upsilon/Binder/Binder.cs b/Upsilon/Binder/Binder.cs index 8d03728..81589f9 100644 --- a/Upsilon/Binder/Binder.cs +++ b/Upsilon/Binder/Binder.cs @@ -70,6 +70,8 @@ namespace Upsilon.Binder return BindMultiAssignmentStatement((MultiAssignmentStatementSyntax) s); case SyntaxKind.NumericForStatement: return BindNumericForStatement((NumericForStatementSyntax) s); + case SyntaxKind.BreakStatement: + return new BoundBreakStatement(); } throw new NotImplementedException(s.Kind.ToString()); diff --git a/Upsilon/Binder/BoundKind.cs b/Upsilon/Binder/BoundKind.cs index a160b8d..b4e90e0 100644 --- a/Upsilon/Binder/BoundKind.cs +++ b/Upsilon/Binder/BoundKind.cs @@ -11,6 +11,7 @@ namespace Upsilon.Binder BoundFunctionCallExpression, BoundTableExpression, BoundIndexExpression, + BoundFunctionExpression, // Statements BoundAssignmentStatement, @@ -18,13 +19,13 @@ namespace Upsilon.Binder BoundBlockStatement, BoundIfStatement, BoundElseStatement, - BoundFunctionExpression, BoundPromise, BoundReturnStatement, BoundFunctionAssignmentStatement, BoundTableAssigmentStatement, BoundFullstopIndexExpression, BoundMultiAssignmentStatement, - BoundNumericForStatement + BoundNumericForStatement, + BoundBreakStatement } } \ No newline at end of file diff --git a/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs b/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs new file mode 100644 index 0000000..8a8872b --- /dev/null +++ b/Upsilon/Binder/BoundStatements/BoundBreakStatement.cs @@ -0,0 +1,7 @@ +namespace Upsilon.Binder +{ + public class BoundBreakStatement : BoundStatement + { + public override BoundKind Kind => BoundKind.BoundBreakStatement; + } +} \ No newline at end of file diff --git a/Upsilon/Evaluator/Evaluator.cs b/Upsilon/Evaluator/Evaluator.cs index 4ab5872..6c0ac0e 100644 --- a/Upsilon/Evaluator/Evaluator.cs +++ b/Upsilon/Evaluator/Evaluator.cs @@ -17,6 +17,7 @@ namespace Upsilon.Evaluator private LuaType _returnValue; internal EvaluationScope Scope { get; private set; } private bool HasReturned { get; set; } + private bool HasBroken { get; set; } internal Evaluator(Diagnostics diagnostics, Dictionary variables) { @@ -132,6 +133,9 @@ namespace Upsilon.Evaluator case BoundKind.BoundNumericForStatement: EvaluateNumericForStatement((BoundNumericForStatement) e); break; + case BoundKind.BoundBreakStatement: + HasBroken = true; + return; default: EvaluateExpressionStatement((BoundExpressionStatement) e); break; @@ -332,30 +336,27 @@ namespace Upsilon.Evaluator { if (HasReturned) return; + if (HasBroken) + return; EvaluateStatement(boundStatement); } } private void EvaluateBoundIfStatement(BoundIfStatement boundBlockStatement) { - var innerEvaluator = new Evaluator(_diagnostics, Scope); - var condition = innerEvaluator.EvaluateExpression(boundBlockStatement.Condition.Expression); + var condition = EvaluateExpression(boundBlockStatement.Condition.Expression); if ((LuaBoolean) condition) { - innerEvaluator.EvaluateStatement(boundBlockStatement.Block); + EvaluateStatement(boundBlockStatement.Block); } else if (boundBlockStatement.NextElseIf != null) { - innerEvaluator.EvaluateStatement(boundBlockStatement.NextElseIf); + EvaluateStatement(boundBlockStatement.NextElseIf); } else if (boundBlockStatement.ElseStatement != null) { - innerEvaluator.EvaluateStatement(boundBlockStatement.ElseStatement.Block); + EvaluateStatement(boundBlockStatement.ElseStatement.Block); } - HasReturned = innerEvaluator.HasReturned; - if (HasReturned) - _returnValue = innerEvaluator._returnValue; - _lastValue = innerEvaluator._lastValue; } private void EvaluateBoundFunctionAssigmentStatement(BoundFunctionAssignmentStatement e) @@ -508,6 +509,8 @@ namespace Upsilon.Evaluator for (; startVal.Value <= stopVal.Value; startVal.Value = startVal.Value + step) { innerEvaluator.EvaluateBoundBlockStatement(e.Block); + if (innerEvaluator.HasBroken) + break; } } else if (step < 0) @@ -515,6 +518,8 @@ namespace Upsilon.Evaluator for (; startVal.Value >= stopVal.Value; startVal.Value = startVal.Value + step) { innerEvaluator.EvaluateBoundBlockStatement(e.Block); + if (innerEvaluator.HasBroken) + break; } } } diff --git a/Upsilon/Parser/Parser.cs b/Upsilon/Parser/Parser.cs index 551bf47..c1e03cc 100644 --- a/Upsilon/Parser/Parser.cs +++ b/Upsilon/Parser/Parser.cs @@ -91,6 +91,10 @@ namespace Upsilon.Parser { return ParseForStatement(); } + if (Current.Kind == SyntaxKind.BreakKeyword) + { + return new BreakStatementSyntax(NextToken()); + } return ParseExpressionStatement(); } diff --git a/Upsilon/Parser/StatementSyntax/BreakStatementSyntax.cs b/Upsilon/Parser/StatementSyntax/BreakStatementSyntax.cs new file mode 100644 index 0000000..1cd7d9e --- /dev/null +++ b/Upsilon/Parser/StatementSyntax/BreakStatementSyntax.cs @@ -0,0 +1,19 @@ +using System.Collections.Generic; + +namespace Upsilon.Parser +{ + public class BreakStatementSyntax : StatementSyntax + { + public SyntaxToken BreakKeyword { get; } + public BreakStatementSyntax(SyntaxToken breakKeyword) + { + BreakKeyword = breakKeyword; + } + + public override SyntaxKind Kind => SyntaxKind.BreakStatement; + public override IEnumerable ChildNodes() + { + yield return BreakKeyword; + } + } +} \ No newline at end of file diff --git a/Upsilon/Parser/SyntaxKeyWords.cs b/Upsilon/Parser/SyntaxKeyWords.cs index ccc2607..38add46 100644 --- a/Upsilon/Parser/SyntaxKeyWords.cs +++ b/Upsilon/Parser/SyntaxKeyWords.cs @@ -40,6 +40,8 @@ namespace Upsilon.Parser return SyntaxKind.InKeyword; case "do": return SyntaxKind.DoKeyword; + case "break": + return SyntaxKind.BreakKeyword; default: return SyntaxKind.Identifier; } diff --git a/Upsilon/Parser/SyntaxKind.cs b/Upsilon/Parser/SyntaxKind.cs index 8a5104f..8f5ff18 100644 --- a/Upsilon/Parser/SyntaxKind.cs +++ b/Upsilon/Parser/SyntaxKind.cs @@ -46,6 +46,7 @@ namespace Upsilon.Parser ForKeyword, InKeyword, DoKeyword, + BreakKeyword, Identifier, @@ -76,6 +77,7 @@ namespace Upsilon.Parser ReturnStatement, FunctionAssignmentStatement, TableAssignmentStatement, - NumericForStatement + NumericForStatement, + BreakStatement } } \ No newline at end of file diff --git a/UpsilonTests/GeneralTests/ForLoopTests.cs b/UpsilonTests/GeneralTests/ForLoopTests.cs index 84652f8..bdda217 100644 --- a/UpsilonTests/GeneralTests/ForLoopTests.cs +++ b/UpsilonTests/GeneralTests/ForLoopTests.cs @@ -59,5 +59,25 @@ return a"; Assert.Equal(15, result); } + [Fact] + public void NumericForLoopBreakTest() + { + const string input = @" +a = 0 +for i=0,10 do + a = a + i + if i == 5 then + break + end +end +return a +"; + var script = new Script(input, BoundScope, StaticScope); + Assert.Empty(script.Diagnostics.Messages); + var result = script.Evaluate(); + Assert.Empty(script.Diagnostics.Messages); + Assert.Equal(15, result); + } + } } \ No newline at end of file diff --git a/UpsilonTests/GeneralTests/ScopeTests.cs b/UpsilonTests/GeneralTests/ScopeTests.cs index 206f5d2..f3aafd0 100644 --- a/UpsilonTests/GeneralTests/ScopeTests.cs +++ b/UpsilonTests/GeneralTests/ScopeTests.cs @@ -14,9 +14,10 @@ namespace UpsilonTests.GeneralTests { const string input = @" a = 10 -if true then +function testFunc() local a = 100 end +testFunc() "; var script = new Script(input, BoundScope, StaticScope); Assert.Empty(script.Diagnostics.Messages);