From df79489d4d1317ead06949bd86551294fe2a5e73 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Fri, 28 Jun 2019 13:28:39 +0200 Subject: [PATCH] Implements while loop --- src/Binder/Binder.cpp | 15 ++++++++++ src/Binder/Binder.hpp | 10 ++----- src/Binder/BoundStatements/BoundStatement.hpp | 28 +++++++++++++++++- src/Evaluator/Evaluator.cpp | 29 ++++++++++++++++--- src/Evaluator/Evaluator.hpp | 1 + .../ParsedStatements/ParsedStatement.hpp | 29 ++++++++++++++++++- src/Parser/Parser.cpp | 16 +++++++++- src/Parser/Parser.hpp | 15 +--------- tests/integration/LoopTests.cpp | 28 ++++++++++++++++++ 9 files changed, 142 insertions(+), 29 deletions(-) diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index 0b328d8..04ebb4f 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -48,6 +48,8 @@ namespace Porygon::Binder { return this->BindNumericalForStatement(statement); case ParsedStatementKind::GenericFor: return this -> BindGenericForStatement(statement); + case ParsedStatementKind::While: + return this -> BindWhileStatement(statement); case ParsedStatementKind::Break: //TODO: Validate we're in a loop return new BoundBreakStatement(); @@ -298,6 +300,18 @@ namespace Porygon::Binder { return new BoundGenericForStatement(keyVariable, valueVariable, boundIterator, boundBlock); } + BoundStatement *Binder::BindWhileStatement(const ParsedStatement *statement) { + auto whileStatement = (ParsedWhileStatement*)statement; + auto boundCondition = this -> BindExpression(whileStatement->GetCondition()); + if (boundCondition->GetType()->GetClass() != TypeClass::Bool){ + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::ConditionNotABool, statement->GetStartPosition(), + statement->GetLength()); + return new BoundBadStatement(); + } + auto boundBlock = this -> BindBlockStatement(whileStatement->GetBlock()); + return new BoundWhileStatement(boundCondition, boundBlock); + } + ///////////////// // Expressions // ///////////////// @@ -674,4 +688,5 @@ namespace Porygon::Binder { return new BoundTableExpression((BoundBlockStatement *) block, tableType, expression->GetStartPosition(), expression->GetLength()); } + } diff --git a/src/Binder/Binder.hpp b/src/Binder/Binder.hpp index 4cbb366..b94487f 100644 --- a/src/Binder/Binder.hpp +++ b/src/Binder/Binder.hpp @@ -30,30 +30,24 @@ namespace Porygon::Binder { BoundStatement *BindConditionalStatement(const ParsedStatement *statement); BoundStatement *BindNumericalForStatement(const ParsedStatement *statement); BoundStatement *BindGenericForStatement(const ParsedStatement *statement); + BoundStatement *BindWhileStatement(const ParsedStatement *statement); // Expressions BoundExpression *BindExpression(const ParsedExpression *expression); - BoundExpression *BindVariableExpression(const VariableExpression *expression); - BoundExpression *BindBinaryOperator(const BinaryExpression *expression); - BoundExpression *BindUnaryOperator(const UnaryExpression *expression); - BoundExpression *BindFunctionCall(const FunctionCallExpression *expression); - BoundExpression *BindIndexExpression(const IndexExpression *expression, bool setter); - BoundExpression *BindNumericalTableExpression(const ParsedNumericalTableExpression *expression); - BoundExpression *BindTableExpression(const ParsedTableExpression *expression); + BoundExpression *BindPeriodIndexExpression(const PeriodIndexExpression *expression, bool setter); public: static BoundScriptStatement * Bind(Porygon::Script *script, const ParsedScriptStatement *s, BoundScope *scriptScope); - BoundExpression *BindPeriodIndexExpression(const PeriodIndexExpression *expression, bool setter); }; } diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index a40ab90..c37c78d 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -12,6 +12,7 @@ using namespace std; namespace Porygon::Binder { enum class BoundStatementKind { Bad, + Break, Script, Block, Expression, @@ -22,7 +23,7 @@ namespace Porygon::Binder { Conditional, NumericalFor, GenericFor, - Break, + While, }; class BoundStatement { @@ -298,6 +299,31 @@ namespace Porygon::Binder { } }; + class BoundWhileStatement : public BoundStatement { + const BoundExpression* _condition; + const BoundStatement *_block; + public: + explicit BoundWhileStatement(const BoundExpression *condition, const BoundStatement *block) + : _condition(condition), _block(block) { + } + + ~BoundWhileStatement() final { + delete _condition; + delete _block; + } + + const BoundStatementKind GetKind() const final { + return BoundStatementKind::While; + } + + const BoundExpression* GetCondition() const{ + return _condition; + } + + const BoundStatement* GetBlock() const{ + return _block; + } + }; } diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index 2b5e805..761d064 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -48,12 +48,16 @@ namespace Porygon::Evaluation { return this->EvaluateNumericalForStatement((BoundNumericalForStatement*)statement); case BoundStatementKind::GenericFor: return this-> EvaluateGenericForStatement((BoundGenericForStatement*)statement); + case BoundStatementKind::While: + return this-> EvaluateWhileStatement((BoundWhileStatement*)statement); + case BoundStatementKind::Break: this -> _hasBroken = true; return; case BoundStatementKind::Bad: throw; } + throw EvaluationException("Evaluating this statement is not supported"); } void Evaluator::EvaluateBlockStatement(const BoundBlockStatement *statement) { @@ -135,10 +139,11 @@ namespace Porygon::Evaluation { auto identifier = statement -> GetIdentifier(); this -> _evaluationScope -> CreateVariable(identifier, nullptr); auto block = (BoundBlockStatement*)statement -> GetBlock(); + auto statements = *block -> GetStatements(); if (step >= 0){ for (long i = start; i <= end; i += step){ this -> _evaluationScope -> SetVariable(identifier, make_shared(i)); - for (auto s: *block->GetStatements()) { + for (auto s: statements) { this->EvaluateStatement(s); if (this->_hasReturned || this -> _hasBroken) break; @@ -149,7 +154,7 @@ namespace Porygon::Evaluation { } else{ for (long i = start; i >= end; i += step){ this -> _evaluationScope -> SetVariable(identifier, make_shared(i)); - for (auto s: *block->GetStatements()) { + for (auto s: statements) { this->EvaluateStatement(s); if (this->_hasReturned || this -> _hasBroken) break; @@ -171,7 +176,7 @@ namespace Porygon::Evaluation { if (valueVariable != nullptr) this -> _evaluationScope -> CreateVariable(valueVariable, nullptr); auto block = (BoundBlockStatement*)statement -> GetBlock(); - + auto statements = *block -> GetStatements(); while (iterator->MoveNext()){ auto currentKey = iterator->GetCurrent(); this -> _evaluationScope -> SetVariable(keyVariable, currentKey); @@ -179,7 +184,7 @@ namespace Porygon::Evaluation { auto currentValue = iteratorVal -> IndexValue(currentKey.get()); this -> _evaluationScope -> SetVariable(valueVariable, currentValue); } - for (auto s: *block->GetStatements()) { + for (auto s: statements) { this->EvaluateStatement(s); if (this->_hasReturned || this -> _hasBroken) break; @@ -191,6 +196,22 @@ namespace Porygon::Evaluation { delete iterator; } + void Evaluator::EvaluateWhileStatement(const BoundWhileStatement *statement) { + auto condition = statement -> GetCondition(); + auto block = (BoundBlockStatement*)statement -> GetBlock(); + auto statements = *block -> GetStatements(); + while (this->EvaluateBoolExpression(condition)->EvaluateBool()){ + for (auto s: statements) { + this->EvaluateStatement(s); + if (this->_hasReturned || this -> _hasBroken) + break; + } + if (this->_hasReturned || this -> _hasBroken) + break; + } + this -> _hasBroken = false; + } + ///////////////// // Expressions // ///////////////// diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index 234be82..8dab63a 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -34,6 +34,7 @@ namespace Porygon::Evaluation{ void EvaluateConditionalStatement(const BoundConditionalStatement *statement); void EvaluateNumericalForStatement(const BoundNumericalForStatement *statement); void EvaluateGenericForStatement(const BoundGenericForStatement *statement); + void EvaluateWhileStatement(const BoundWhileStatement *statement); const shared_ptr EvaluateExpression(const BoundExpression *expression); const shared_ptr EvaluateIntegerExpression(const BoundExpression *expression); diff --git a/src/Parser/ParsedStatements/ParsedStatement.hpp b/src/Parser/ParsedStatements/ParsedStatement.hpp index e36125a..1c91afa 100644 --- a/src/Parser/ParsedStatements/ParsedStatement.hpp +++ b/src/Parser/ParsedStatements/ParsedStatement.hpp @@ -13,6 +13,7 @@ namespace Porygon::Parser { enum class ParsedStatementKind : uint8_t { Bad, + Break, Script, Block, Expression, @@ -23,7 +24,7 @@ namespace Porygon::Parser { Conditional, NumericalFor, GenericFor, - Break + While }; class ParsedStatement { @@ -375,5 +376,31 @@ namespace Porygon::Parser { } }; + class ParsedWhileStatement : public ParsedStatement{ + const ParsedExpression* _condition; + const ParsedStatement* _block; + public: + ParsedWhileStatement(const ParsedExpression *condition, const ParsedStatement *block, + unsigned int start, unsigned int length) + : ParsedStatement(start, length), _condition(condition), _block(block) {} + + ~ParsedWhileStatement() final{ + delete _condition; + delete _block; + } + + const ParsedStatementKind GetKind() const final { + return ParsedStatementKind::While; + } + + const ParsedExpression* GetCondition(){ + return _condition; + } + + const ParsedStatement* GetBlock(){ + return _block; + } + }; + } #endif //PORYGONLANG_PARSEDSTATEMENT_HPP diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index 0c9b230..54465e4 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -46,6 +46,8 @@ namespace Porygon::Parser { return this->ParseIfStatement(current); case TokenKind ::ForKeyword: return this->ParseForStatement(); + case TokenKind ::WhileKeyword: + return this->ParseWhileStatement(current); case TokenKind ::BreakKeyword: return new ParsedBreakStatement(current->GetStartPosition(), current -> GetLength()); default: @@ -312,6 +314,19 @@ namespace Porygon::Parser { } } + ParsedStatement *Parser::ParseWhileStatement(const IToken *current) { + auto condition = this -> ParseExpression(this -> Next()); + auto doKeyword = this -> Next(); + if (doKeyword -> GetKind() != TokenKind::DoKeyword){ + this->ScriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::UnexpectedToken, doKeyword->GetStartPosition(), + doKeyword->GetLength()); + return new ParsedBadStatement(doKeyword->GetStartPosition(), doKeyword->GetLength()); + } + auto block = this -> ParseBlock({TokenKind ::EndKeyword}); + auto start = current -> GetStartPosition(); + return new ParsedWhileStatement(condition, block, start, block->GetEndPosition() - start); + } + ///////////////// // Expressions // ///////////////// @@ -597,5 +612,4 @@ namespace Porygon::Parser { return new ParsedTableExpression(block, start, closeToken->GetEndPosition() - start); } } - } \ No newline at end of file diff --git a/src/Parser/Parser.hpp b/src/Parser/Parser.hpp index 30c1088..0519600 100644 --- a/src/Parser/Parser.hpp +++ b/src/Parser/Parser.hpp @@ -32,39 +32,26 @@ namespace Porygon::Parser { // Statements ParsedStatement *ParseStatement(const IToken *current); - ParsedStatement *ParseVariableAssignment(const IToken *current); - ParsedStatement *ParseIndexAssignment(ParsedExpression *indexer); - ParsedStatement *ParseBlock(const vector &endTokens, const vector &openStatements = {}); - ParsedStatement *ParseFunctionDeclaration(const IToken *current); - ParsedStatement *ParseReturnStatement(const IToken *current); - ParsedStatement *ParseIfStatement(const IToken *current); - ParsedStatement *ParseForStatement(); ParsedStatement *ParseNumericForStatement(const IToken *current); ParsedStatement *ParseGenericForStatement(const IToken *current); + ParsedStatement *ParseWhileStatement(const IToken *current); // Expressions ParsedExpression *ParseExpression(const IToken *current); - ParsedExpression *ParseBinaryExpression(const IToken *current, OperatorPrecedence parentPrecedence); - ParsedExpression *ParsePrimaryExpression(const IToken *current); - ParsedExpression *ParseParenthesizedExpression(const IToken *current); - ParsedExpression *ParseFunctionCallExpression(ParsedExpression *functionExpression); - ParsedExpression *ParseIndexExpression(ParsedExpression *indexingExpression); - ParsedExpression *ParsePeriodIndexExpression(ParsedExpression *indexingExpression); - ParsedExpression *ParseTableExpression(const IToken *current); public: diff --git a/tests/integration/LoopTests.cpp b/tests/integration/LoopTests.cpp index 6a3b5a0..a844bfb 100644 --- a/tests/integration/LoopTests.cpp +++ b/tests/integration/LoopTests.cpp @@ -122,5 +122,33 @@ end delete script; } +TEST_CASE( "While loop", "[integration]" ) { + auto script = Script::Create(uR"( +result = 0 +while result < 5 do + result = result + 1 +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateInteger() == 5); + delete script; +} + +TEST_CASE( "While loop break", "[integration]" ) { + auto script = Script::Create(uR"( +result = 0 +while true do + result = result + 1 + if result >= 5 then break end +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateInteger() == 5); + delete script; +} #endif