Support "next" statement, skipping the remainder of the loop block.
continuous-integration/drone/push Build is passing
Details
continuous-integration/drone/push Build is passing
Details
This commit is contained in:
parent
f4277d47c3
commit
2cd787c536
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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());
|
||||
}
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -56,6 +56,7 @@ namespace Porygon::Parser {
|
|||
ThenKeyword,
|
||||
TrueKeyword,
|
||||
WhileKeyword,
|
||||
NextKeyword,
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue