Support break statements
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2019-06-27 15:55:46 +02:00
parent 46197e0a6e
commit 3367e60ae5
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
8 changed files with 80 additions and 9 deletions

View File

@ -48,6 +48,9 @@ namespace Porygon::Binder {
return this->BindNumericalForStatement(statement); return this->BindNumericalForStatement(statement);
case ParsedStatementKind::GenericFor: case ParsedStatementKind::GenericFor:
return this -> BindGenericForStatement(statement); return this -> BindGenericForStatement(statement);
case ParsedStatementKind::Break:
//TODO: Validate we're in a loop
return new BoundBreakStatement();
case ParsedStatementKind::Bad: case ParsedStatementKind::Bad:
return new BoundBadStatement(); return new BoundBadStatement();
} }

View File

@ -22,6 +22,7 @@ namespace Porygon::Binder {
Conditional, Conditional,
NumericalFor, NumericalFor,
GenericFor, GenericFor,
Break,
}; };
class BoundStatement { class BoundStatement {
@ -38,6 +39,13 @@ namespace Porygon::Binder {
} }
}; };
class BoundBreakStatement : public BoundStatement {
public:
const BoundStatementKind GetKind() const final {
return BoundStatementKind::Break;
}
};
class BoundBlockStatement : public BoundStatement { class BoundBlockStatement : public BoundStatement {
const vector<BoundStatement *> _statements; const vector<BoundStatement *> _statements;
public: public:

View File

@ -48,7 +48,9 @@ namespace Porygon::Evaluation {
return this->EvaluateNumericalForStatement((BoundNumericalForStatement*)statement); return this->EvaluateNumericalForStatement((BoundNumericalForStatement*)statement);
case BoundStatementKind::GenericFor: case BoundStatementKind::GenericFor:
return this-> EvaluateGenericForStatement((BoundGenericForStatement*)statement); return this-> EvaluateGenericForStatement((BoundGenericForStatement*)statement);
case BoundStatementKind::Break:
this -> _hasBroken = true;
return;
case BoundStatementKind::Bad: case BoundStatementKind::Bad:
throw; throw;
} }
@ -138,20 +140,25 @@ namespace Porygon::Evaluation {
this -> _evaluationScope -> SetVariable(identifier, make_shared<IntegerEvalValue>(i)); this -> _evaluationScope -> SetVariable(identifier, make_shared<IntegerEvalValue>(i));
for (auto s: *block->GetStatements()) { for (auto s: *block->GetStatements()) {
this->EvaluateStatement(s); this->EvaluateStatement(s);
if (this->_hasReturned) if (this->_hasReturned || this -> _hasBroken)
break; break;
} }
if (this->_hasReturned || this -> _hasBroken)
break;
} }
} else{ } else{
for (long i = start; i >= end; i += step){ for (long i = start; i >= end; i += step){
this -> _evaluationScope -> SetVariable(identifier, make_shared<IntegerEvalValue>(i)); this -> _evaluationScope -> SetVariable(identifier, make_shared<IntegerEvalValue>(i));
for (auto s: *block->GetStatements()) { for (auto s: *block->GetStatements()) {
this->EvaluateStatement(s); this->EvaluateStatement(s);
if (this->_hasReturned) if (this->_hasReturned || this -> _hasBroken)
break; break;
} }
if (this->_hasReturned || this -> _hasBroken)
break;
} }
} }
this -> _hasBroken = false;
} }
void Evaluator::EvaluateGenericForStatement(const BoundGenericForStatement *statement) { void Evaluator::EvaluateGenericForStatement(const BoundGenericForStatement *statement) {
@ -172,8 +179,15 @@ namespace Porygon::Evaluation {
auto currentValue = iteratorVal -> IndexValue(currentKey.get()); auto currentValue = iteratorVal -> IndexValue(currentKey.get());
this -> _evaluationScope -> SetVariable(valueVariable, currentValue); this -> _evaluationScope -> SetVariable(valueVariable, currentValue);
} }
this -> EvaluateBlockStatement(block); for (auto s: *block->GetStatements()) {
this->EvaluateStatement(s);
if (this->_hasReturned || this -> _hasBroken)
break;
}
if (this->_hasReturned || this -> _hasBroken)
break;
} }
this -> _hasBroken = false;
delete iterator; delete iterator;
} }
@ -433,7 +447,7 @@ namespace Porygon::Evaluation {
auto type = dynamic_pointer_cast<TableScriptType>(tableExpression->GetType()); auto type = dynamic_pointer_cast<TableScriptType>(tableExpression->GetType());
auto declaredVars = type->GetValues(); auto declaredVars = type->GetValues();
auto variables = make_shared<map<Utilities::HashedString, shared_ptr<EvalValue>>>(); auto variables = make_shared<map<Utilities::HashedString, shared_ptr<EvalValue>>>();
for (auto i : *declaredVars) { for (const auto& i : *declaredVars) {
variables->insert({i.first, nullptr}); variables->insert({i.first, nullptr});
} }
auto evaluator = make_shared<EvaluationScope>(variables.get(), type->GetLocalVariableCount()); auto evaluator = make_shared<EvaluationScope>(variables.get(), type->GetLocalVariableCount());

View File

@ -18,6 +18,7 @@ namespace Porygon::Evaluation{
shared_ptr<EvalValue> _returnValue; shared_ptr<EvalValue> _returnValue;
map<Utilities::HashedString, shared_ptr<EvalValue>>* _scriptVariables; map<Utilities::HashedString, shared_ptr<EvalValue>>* _scriptVariables;
bool _hasReturned; bool _hasReturned;
bool _hasBroken;
shared_ptr<EvalValue> _lastValue; shared_ptr<EvalValue> _lastValue;
//Porygon::Script* _scriptData; //Porygon::Script* _scriptData;
@ -60,6 +61,7 @@ namespace Porygon::Evaluation{
explicit Evaluator(map<Utilities::HashedString, shared_ptr<EvalValue>>* scriptVariables){ explicit Evaluator(map<Utilities::HashedString, shared_ptr<EvalValue>>* scriptVariables){
_scriptVariables = scriptVariables; _scriptVariables = scriptVariables;
_hasReturned = false; _hasReturned = false;
_hasBroken = false;
_returnValue = nullptr; _returnValue = nullptr;
_evaluationScope = nullptr; _evaluationScope = nullptr;
} }

View File

@ -22,7 +22,8 @@ namespace Porygon::Parser {
Return, Return,
Conditional, Conditional,
NumericalFor, NumericalFor,
GenericFor GenericFor,
Break
}; };
class ParsedStatement { class ParsedStatement {
@ -59,6 +60,15 @@ namespace Porygon::Parser {
} }
}; };
class ParsedBreakStatement : public ParsedStatement{
public:
ParsedBreakStatement(unsigned int start, unsigned int length) : ParsedStatement(start, length) {};
const ParsedStatementKind GetKind() const final {
return ParsedStatementKind::Break;
}
};
class ParsedBlockStatement : public ParsedStatement { class ParsedBlockStatement : public ParsedStatement {
const std::vector<const ParsedStatement *> _statements; const std::vector<const ParsedStatement *> _statements;
public: public:

View File

@ -45,7 +45,9 @@ namespace Porygon::Parser {
case TokenKind::IfKeyword: case TokenKind::IfKeyword:
return this->ParseIfStatement(current); return this->ParseIfStatement(current);
case TokenKind ::ForKeyword: case TokenKind ::ForKeyword:
return this->ParseForStatement(current); return this->ParseForStatement();
case TokenKind ::BreakKeyword:
return new ParsedBreakStatement(current->GetStartPosition(), current -> GetLength());
default: default:
break; break;
} }
@ -225,7 +227,7 @@ namespace Porygon::Parser {
return new ParsedConditionalStatement(condition, block, start, block->GetEndPosition() - start); return new ParsedConditionalStatement(condition, block, start, block->GetEndPosition() - start);
} }
ParsedStatement *Parser::ParseForStatement(const IToken *current) { ParsedStatement *Parser::ParseForStatement() {
auto identifier = this -> Next(); auto identifier = this -> Next();
if (this -> Peek()->GetKind() == TokenKind::AssignmentToken){ if (this -> Peek()->GetKind() == TokenKind::AssignmentToken){
return ParseNumericForStatement(identifier); return ParseNumericForStatement(identifier);

View File

@ -45,7 +45,7 @@ namespace Porygon::Parser {
ParsedStatement *ParseIfStatement(const IToken *current); ParsedStatement *ParseIfStatement(const IToken *current);
ParsedStatement *ParseForStatement(const IToken *current); ParsedStatement *ParseForStatement();
ParsedStatement *ParseNumericForStatement(const IToken *current); ParsedStatement *ParseNumericForStatement(const IToken *current);
ParsedStatement *ParseGenericForStatement(const IToken *current); ParsedStatement *ParseGenericForStatement(const IToken *current);

View File

@ -60,6 +60,22 @@ end
delete script; delete script;
} }
TEST_CASE( "Numerical for loop, break", "[integration]" ) {
auto script = Script::Create(uR"(
result = 0
for i = 0,5 do
if i > 3 then break end
result = result + i
end
)");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
auto var = script->GetVariable(u"result");
REQUIRE(var->EvaluateInteger() == 6);
delete script;
}
TEST_CASE( "Generic for loop over simple numerical table, get keys", "[integration]" ) { TEST_CASE( "Generic for loop over simple numerical table, get keys", "[integration]" ) {
auto script = Script::Create(uR"( auto script = Script::Create(uR"(
local table = {1, 3, 5, 7, 9} local table = {1, 3, 5, 7, 9}
@ -90,5 +106,21 @@ end
delete script; delete script;
} }
TEST_CASE( "Generic for loop over simple numerical table, break", "[integration]" ) {
auto script = Script::Create(uR"(
local table = {1, 3, 5, 7, 9}
result = 0
for i,v in table do
if i > 3 then break end
result = result + v
end
)");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
auto var = script->GetVariable(u"result");
REQUIRE(var->EvaluateInteger() == 9);
delete script;
}
#endif #endif