Support "next" statement, skipping the remainder of the loop block.
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2019-09-22 13:49:37 +02:00
parent f4277d47c3
commit 2cd787c536
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
9 changed files with 70 additions and 7 deletions

View File

@ -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();
}

View File

@ -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<const BoundStatement *> _statements;
public:

View File

@ -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;
}

View File

@ -19,8 +19,9 @@ namespace Porygon::Evaluation{
class Evaluator {
EvalValuePointer _returnValue;
map<Utilities::HashedString, EvalValuePointer>* _scriptVariables;
bool _hasReturned;
bool _hasBroken;
bool _hasReturned = false;
bool _hasBroken = false;
bool _hasContinued = false;
const Porygon::ScriptOptions* _scriptOptions;
shared_ptr<EvaluationScope> _evaluationScope;

View File

@ -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());
}

View File

@ -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<const ParsedStatement *> _statements;
public:

View File

@ -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;
}

View File

@ -56,6 +56,7 @@ namespace Porygon::Parser {
ThenKeyword,
TrueKeyword,
WhileKeyword,
NextKeyword,
};
}

View File

@ -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