diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index 54b1a51..0b328d8 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -48,6 +48,9 @@ namespace Porygon::Binder { return this->BindNumericalForStatement(statement); case ParsedStatementKind::GenericFor: return this -> BindGenericForStatement(statement); + case ParsedStatementKind::Break: + //TODO: Validate we're in a loop + return new BoundBreakStatement(); case ParsedStatementKind::Bad: return new BoundBadStatement(); } diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index 62fe4a6..a40ab90 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -22,6 +22,7 @@ namespace Porygon::Binder { Conditional, NumericalFor, GenericFor, + Break, }; class BoundStatement { @@ -38,6 +39,13 @@ namespace Porygon::Binder { } }; + class BoundBreakStatement : public BoundStatement { + public: + const BoundStatementKind GetKind() const final { + return BoundStatementKind::Break; + } + }; + class BoundBlockStatement : public BoundStatement { const vector _statements; public: diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index a73c8c7..2b5e805 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -48,7 +48,9 @@ namespace Porygon::Evaluation { return this->EvaluateNumericalForStatement((BoundNumericalForStatement*)statement); case BoundStatementKind::GenericFor: return this-> EvaluateGenericForStatement((BoundGenericForStatement*)statement); - + case BoundStatementKind::Break: + this -> _hasBroken = true; + return; case BoundStatementKind::Bad: throw; } @@ -138,20 +140,25 @@ namespace Porygon::Evaluation { this -> _evaluationScope -> SetVariable(identifier, make_shared(i)); for (auto s: *block->GetStatements()) { this->EvaluateStatement(s); - if (this->_hasReturned) + if (this->_hasReturned || this -> _hasBroken) break; } + if (this->_hasReturned || this -> _hasBroken) + break; } } else{ for (long i = start; i >= end; i += step){ this -> _evaluationScope -> SetVariable(identifier, make_shared(i)); for (auto s: *block->GetStatements()) { this->EvaluateStatement(s); - if (this->_hasReturned) + if (this->_hasReturned || this -> _hasBroken) break; } + if (this->_hasReturned || this -> _hasBroken) + break; } } + this -> _hasBroken = false; } void Evaluator::EvaluateGenericForStatement(const BoundGenericForStatement *statement) { @@ -172,8 +179,15 @@ namespace Porygon::Evaluation { auto currentValue = iteratorVal -> IndexValue(currentKey.get()); this -> _evaluationScope -> SetVariable(valueVariable, currentValue); } - this -> EvaluateBlockStatement(block); + for (auto s: *block->GetStatements()) { + this->EvaluateStatement(s); + if (this->_hasReturned || this -> _hasBroken) + break; + } + if (this->_hasReturned || this -> _hasBroken) + break; } + this -> _hasBroken = false; delete iterator; } @@ -433,7 +447,7 @@ namespace Porygon::Evaluation { auto type = dynamic_pointer_cast(tableExpression->GetType()); auto declaredVars = type->GetValues(); auto variables = make_shared>>(); - for (auto i : *declaredVars) { + for (const auto& i : *declaredVars) { variables->insert({i.first, nullptr}); } auto evaluator = make_shared(variables.get(), type->GetLocalVariableCount()); diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index 5ab4d9c..234be82 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -18,6 +18,7 @@ namespace Porygon::Evaluation{ shared_ptr _returnValue; map>* _scriptVariables; bool _hasReturned; + bool _hasBroken; shared_ptr _lastValue; //Porygon::Script* _scriptData; @@ -60,6 +61,7 @@ namespace Porygon::Evaluation{ explicit Evaluator(map>* scriptVariables){ _scriptVariables = scriptVariables; _hasReturned = false; + _hasBroken = false; _returnValue = nullptr; _evaluationScope = nullptr; } diff --git a/src/Parser/ParsedStatements/ParsedStatement.hpp b/src/Parser/ParsedStatements/ParsedStatement.hpp index 574c7cd..e36125a 100644 --- a/src/Parser/ParsedStatements/ParsedStatement.hpp +++ b/src/Parser/ParsedStatements/ParsedStatement.hpp @@ -22,7 +22,8 @@ namespace Porygon::Parser { Return, Conditional, NumericalFor, - GenericFor + GenericFor, + Break }; class ParsedStatement { @@ -59,6 +60,15 @@ namespace Porygon::Parser { } }; + class ParsedBreakStatement : public ParsedStatement{ + public: + ParsedBreakStatement(unsigned int start, unsigned int length) : ParsedStatement(start, length) {}; + + const ParsedStatementKind GetKind() const final { + return ParsedStatementKind::Break; + } + }; + class ParsedBlockStatement : public ParsedStatement { const std::vector _statements; public: diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index dc99c93..0c9b230 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -45,7 +45,9 @@ namespace Porygon::Parser { case TokenKind::IfKeyword: return this->ParseIfStatement(current); case TokenKind ::ForKeyword: - return this->ParseForStatement(current); + return this->ParseForStatement(); + case TokenKind ::BreakKeyword: + return new ParsedBreakStatement(current->GetStartPosition(), current -> GetLength()); default: break; } @@ -225,7 +227,7 @@ namespace Porygon::Parser { return new ParsedConditionalStatement(condition, block, start, block->GetEndPosition() - start); } - ParsedStatement *Parser::ParseForStatement(const IToken *current) { + ParsedStatement *Parser::ParseForStatement() { auto identifier = this -> Next(); if (this -> Peek()->GetKind() == TokenKind::AssignmentToken){ return ParseNumericForStatement(identifier); diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index e3ab72e..30c1088 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -45,7 +45,7 @@ namespace Porygon::Parser { ParsedStatement *ParseIfStatement(const IToken *current); - ParsedStatement *ParseForStatement(const IToken *current); + ParsedStatement *ParseForStatement(); ParsedStatement *ParseNumericForStatement(const IToken *current); ParsedStatement *ParseGenericForStatement(const IToken *current); diff --git a/tests/integration/LoopTests.cpp b/tests/integration/LoopTests.cpp index 572ee98..6a3b5a0 100644 --- a/tests/integration/LoopTests.cpp +++ b/tests/integration/LoopTests.cpp @@ -60,6 +60,22 @@ end delete script; } +TEST_CASE( "Numerical for loop, break", "[integration]" ) { + auto script = Script::Create(uR"( +result = 0 +for i = 0,5 do + if i > 3 then break end + result = result + i +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateInteger() == 6); + delete script; +} + + TEST_CASE( "Generic for loop over simple numerical table, get keys", "[integration]" ) { auto script = Script::Create(uR"( local table = {1, 3, 5, 7, 9} @@ -90,5 +106,21 @@ end delete script; } +TEST_CASE( "Generic for loop over simple numerical table, break", "[integration]" ) { + auto script = Script::Create(uR"( +local table = {1, 3, 5, 7, 9} +result = 0 +for i,v in table do + if i > 3 then break end + result = result + v +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateInteger() == 9); + delete script; +} + #endif