diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index 6669cbf..72f6f68 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -54,6 +54,8 @@ namespace Porygon::Binder { case ParsedStatementKind::Break: //TODO: Validate we're in a loop return new BoundBreakStatement(); + case ParsedStatementKind::Next: + return new BoundNextStatement(); case ParsedStatementKind::Bad: return new BoundBadStatement(); } diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index f4a8d97..80ab825 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -12,6 +12,7 @@ namespace Porygon::Binder { enum class BoundStatementKind : uint8_t { Bad, Break, + Next, Script, Block, Expression, @@ -67,6 +68,19 @@ namespace Porygon::Binder { } }; + class BoundNextStatement : public BoundStatement { + public: + [[nodiscard]] + inline BoundStatementKind GetKind() const final { + return BoundStatementKind::Next; + } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + DrawIndents(stream, indents); + stream << "NextStatement"; + } + }; + class BoundBlockStatement : public BoundStatement { const vector _statements; public: diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index 7f1d869..7f6feca 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -61,6 +61,9 @@ namespace Porygon::Evaluation { case BoundStatementKind::Break: this -> _hasBroken = true; return; + case BoundStatementKind::Next: + this -> _hasContinued = true; + return; case BoundStatementKind::Bad: throw; } @@ -166,9 +169,10 @@ namespace Porygon::Evaluation { this -> _evaluationScope -> SetVariable(identifier, new NumericEvalValue(i)); for (auto s: statements) { this->EvaluateStatement(s); - if (this->_hasReturned || this -> _hasBroken) + if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued) break; } + this -> _hasContinued = false; if (this->_hasReturned || this -> _hasBroken) break; } @@ -177,9 +181,10 @@ namespace Porygon::Evaluation { this -> _evaluationScope -> SetVariable(identifier, new NumericEvalValue(i)); for (auto s: statements) { this->EvaluateStatement(s); - if (this->_hasReturned || this -> _hasBroken) + if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued) break; } + this -> _hasContinued = false; if (this->_hasReturned || this -> _hasBroken) break; } @@ -207,9 +212,10 @@ namespace Porygon::Evaluation { } for (auto s: statements) { this->EvaluateStatement(s); - if (this->_hasReturned || this -> _hasBroken) + if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued) break; } + this -> _hasContinued = false; if (this->_hasReturned || this -> _hasBroken) break; } @@ -224,9 +230,10 @@ namespace Porygon::Evaluation { while (this->EvaluateExpression(condition)->EvaluateBool()){ for (auto s: statements) { this->EvaluateStatement(s); - if (this->_hasReturned || this -> _hasBroken) + if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued) break; } + this -> _hasContinued = false; if (this->_hasReturned || this -> _hasBroken) break; } diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index b072f85..446fa77 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -19,8 +19,9 @@ namespace Porygon::Evaluation{ class Evaluator { EvalValuePointer _returnValue; map* _scriptVariables; - bool _hasReturned; - bool _hasBroken; + bool _hasReturned = false; + bool _hasBroken = false; + bool _hasContinued = false; const Porygon::ScriptOptions* _scriptOptions; shared_ptr _evaluationScope; diff --git a/src/Parser/Lexer.cpp b/src/Parser/Lexer.cpp index c8f5b49..9a5e881 100644 --- a/src/Parser/Lexer.cpp +++ b/src/Parser/Lexer.cpp @@ -268,6 +268,8 @@ namespace Porygon::Parser { return new SimpleToken(TokenKind::TrueKeyword, start, 4); case HashedString::ConstHash("while"): return new SimpleToken(TokenKind::WhileKeyword, start, 5); + case HashedString::ConstHash("next"): + return new SimpleToken(TokenKind::NextKeyword, start, 4); default: return new IdentifierToken(HashedString(new u16string(s)), start, s.length()); } diff --git a/src/Parser/ParsedStatements/ParsedStatement.hpp b/src/Parser/ParsedStatements/ParsedStatement.hpp index ab5a159..c6f1d5d 100644 --- a/src/Parser/ParsedStatements/ParsedStatement.hpp +++ b/src/Parser/ParsedStatements/ParsedStatement.hpp @@ -14,6 +14,7 @@ namespace Porygon::Parser { enum class ParsedStatementKind : uint8_t { Bad, Break, + Next, Script, Block, Expression, @@ -24,7 +25,7 @@ namespace Porygon::Parser { Conditional, NumericalFor, GenericFor, - While + While, }; class ParsedStatement { @@ -70,6 +71,15 @@ namespace Porygon::Parser { } }; + class ParsedNextStatement : public ParsedStatement{ + public: + ParsedNextStatement(unsigned int start, unsigned int length) : ParsedStatement(start, length) {}; + + [[nodiscard]] inline ParsedStatementKind GetKind() const final { + return ParsedStatementKind::Next; + } + }; + class ParsedBlockStatement : public ParsedStatement { const std::vector _statements; public: diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index 8140dae..4438e1c 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -58,6 +58,8 @@ namespace Porygon::Parser { return this->ParseWhileStatement(current); case TokenKind ::BreakKeyword: return new ParsedBreakStatement(current->GetStartPosition(), current -> GetLength()); + case TokenKind::NextKeyword: + return new ParsedNextStatement(current->GetStartPosition(), current -> GetLength()); default: break; } diff --git a/src/Parser/TokenKind.hpp b/src/Parser/TokenKind.hpp index 3ab41b9..ec0785e 100644 --- a/src/Parser/TokenKind.hpp +++ b/src/Parser/TokenKind.hpp @@ -56,6 +56,7 @@ namespace Porygon::Parser { ThenKeyword, TrueKeyword, WhileKeyword, + NextKeyword, }; } diff --git a/tests/integration/LoopTests.cpp b/tests/integration/LoopTests.cpp index 8171ddc..22282fe 100644 --- a/tests/integration/LoopTests.cpp +++ b/tests/integration/LoopTests.cpp @@ -161,4 +161,28 @@ end delete var; } +TEST_CASE( "While loop next", "[integration]" ) { + auto script = Script::Create(uR"( +result = 0 +runCount = 0 +while true do + result = result + 1 + if (result == 3) then next end + runCount = runCount + 1 + if result >= 5 then break end +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateInteger() == 5); + auto runCount = script->GetVariable(u"runCount"); + REQUIRE(runCount->EvaluateInteger() == 4); + + delete script; + delete var; + delete runCount; +} + + #endif