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:
|
case ParsedStatementKind::Break:
|
||||||
//TODO: Validate we're in a loop
|
//TODO: Validate we're in a loop
|
||||||
return new BoundBreakStatement();
|
return new BoundBreakStatement();
|
||||||
|
case ParsedStatementKind::Next:
|
||||||
|
return new BoundNextStatement();
|
||||||
case ParsedStatementKind::Bad:
|
case ParsedStatementKind::Bad:
|
||||||
return new BoundBadStatement();
|
return new BoundBadStatement();
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,6 +12,7 @@ namespace Porygon::Binder {
|
||||||
enum class BoundStatementKind : uint8_t {
|
enum class BoundStatementKind : uint8_t {
|
||||||
Bad,
|
Bad,
|
||||||
Break,
|
Break,
|
||||||
|
Next,
|
||||||
Script,
|
Script,
|
||||||
Block,
|
Block,
|
||||||
Expression,
|
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 {
|
class BoundBlockStatement : public BoundStatement {
|
||||||
const vector<const BoundStatement *> _statements;
|
const vector<const BoundStatement *> _statements;
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -61,6 +61,9 @@ namespace Porygon::Evaluation {
|
||||||
case BoundStatementKind::Break:
|
case BoundStatementKind::Break:
|
||||||
this -> _hasBroken = true;
|
this -> _hasBroken = true;
|
||||||
return;
|
return;
|
||||||
|
case BoundStatementKind::Next:
|
||||||
|
this -> _hasContinued = true;
|
||||||
|
return;
|
||||||
case BoundStatementKind::Bad:
|
case BoundStatementKind::Bad:
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
|
@ -166,9 +169,10 @@ namespace Porygon::Evaluation {
|
||||||
this -> _evaluationScope -> SetVariable(identifier, new NumericEvalValue(i));
|
this -> _evaluationScope -> SetVariable(identifier, new NumericEvalValue(i));
|
||||||
for (auto s: statements) {
|
for (auto s: statements) {
|
||||||
this->EvaluateStatement(s);
|
this->EvaluateStatement(s);
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this -> _hasContinued = false;
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -177,9 +181,10 @@ namespace Porygon::Evaluation {
|
||||||
this -> _evaluationScope -> SetVariable(identifier, new NumericEvalValue(i));
|
this -> _evaluationScope -> SetVariable(identifier, new NumericEvalValue(i));
|
||||||
for (auto s: statements) {
|
for (auto s: statements) {
|
||||||
this->EvaluateStatement(s);
|
this->EvaluateStatement(s);
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this -> _hasContinued = false;
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -207,9 +212,10 @@ namespace Porygon::Evaluation {
|
||||||
}
|
}
|
||||||
for (auto s: statements) {
|
for (auto s: statements) {
|
||||||
this->EvaluateStatement(s);
|
this->EvaluateStatement(s);
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this -> _hasContinued = false;
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@ -224,9 +230,10 @@ namespace Porygon::Evaluation {
|
||||||
while (this->EvaluateExpression(condition)->EvaluateBool()){
|
while (this->EvaluateExpression(condition)->EvaluateBool()){
|
||||||
for (auto s: statements) {
|
for (auto s: statements) {
|
||||||
this->EvaluateStatement(s);
|
this->EvaluateStatement(s);
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken || this -> _hasContinued)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
this -> _hasContinued = false;
|
||||||
if (this->_hasReturned || this -> _hasBroken)
|
if (this->_hasReturned || this -> _hasBroken)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -19,8 +19,9 @@ namespace Porygon::Evaluation{
|
||||||
class Evaluator {
|
class Evaluator {
|
||||||
EvalValuePointer _returnValue;
|
EvalValuePointer _returnValue;
|
||||||
map<Utilities::HashedString, EvalValuePointer>* _scriptVariables;
|
map<Utilities::HashedString, EvalValuePointer>* _scriptVariables;
|
||||||
bool _hasReturned;
|
bool _hasReturned = false;
|
||||||
bool _hasBroken;
|
bool _hasBroken = false;
|
||||||
|
bool _hasContinued = false;
|
||||||
|
|
||||||
const Porygon::ScriptOptions* _scriptOptions;
|
const Porygon::ScriptOptions* _scriptOptions;
|
||||||
shared_ptr<EvaluationScope> _evaluationScope;
|
shared_ptr<EvaluationScope> _evaluationScope;
|
||||||
|
|
|
@ -268,6 +268,8 @@ namespace Porygon::Parser {
|
||||||
return new SimpleToken(TokenKind::TrueKeyword, start, 4);
|
return new SimpleToken(TokenKind::TrueKeyword, start, 4);
|
||||||
case HashedString::ConstHash("while"):
|
case HashedString::ConstHash("while"):
|
||||||
return new SimpleToken(TokenKind::WhileKeyword, start, 5);
|
return new SimpleToken(TokenKind::WhileKeyword, start, 5);
|
||||||
|
case HashedString::ConstHash("next"):
|
||||||
|
return new SimpleToken(TokenKind::NextKeyword, start, 4);
|
||||||
default:
|
default:
|
||||||
return new IdentifierToken(HashedString(new u16string(s)), start, s.length());
|
return new IdentifierToken(HashedString(new u16string(s)), start, s.length());
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,6 +14,7 @@ namespace Porygon::Parser {
|
||||||
enum class ParsedStatementKind : uint8_t {
|
enum class ParsedStatementKind : uint8_t {
|
||||||
Bad,
|
Bad,
|
||||||
Break,
|
Break,
|
||||||
|
Next,
|
||||||
Script,
|
Script,
|
||||||
Block,
|
Block,
|
||||||
Expression,
|
Expression,
|
||||||
|
@ -24,7 +25,7 @@ namespace Porygon::Parser {
|
||||||
Conditional,
|
Conditional,
|
||||||
NumericalFor,
|
NumericalFor,
|
||||||
GenericFor,
|
GenericFor,
|
||||||
While
|
While,
|
||||||
};
|
};
|
||||||
|
|
||||||
class ParsedStatement {
|
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 {
|
class ParsedBlockStatement : public ParsedStatement {
|
||||||
const std::vector<const ParsedStatement *> _statements;
|
const std::vector<const ParsedStatement *> _statements;
|
||||||
public:
|
public:
|
||||||
|
|
|
@ -58,6 +58,8 @@ namespace Porygon::Parser {
|
||||||
return this->ParseWhileStatement(current);
|
return this->ParseWhileStatement(current);
|
||||||
case TokenKind ::BreakKeyword:
|
case TokenKind ::BreakKeyword:
|
||||||
return new ParsedBreakStatement(current->GetStartPosition(), current -> GetLength());
|
return new ParsedBreakStatement(current->GetStartPosition(), current -> GetLength());
|
||||||
|
case TokenKind::NextKeyword:
|
||||||
|
return new ParsedNextStatement(current->GetStartPosition(), current -> GetLength());
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
@ -56,6 +56,7 @@ namespace Porygon::Parser {
|
||||||
ThenKeyword,
|
ThenKeyword,
|
||||||
TrueKeyword,
|
TrueKeyword,
|
||||||
WhileKeyword,
|
WhileKeyword,
|
||||||
|
NextKeyword,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -161,4 +161,28 @@ end
|
||||||
delete var;
|
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
|
#endif
|
||||||
|
|
Loading…
Reference in New Issue