Implements while loop
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2019-06-28 13:28:39 +02:00
parent f7a6ff7d87
commit df79489d4d
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
9 changed files with 142 additions and 29 deletions

View File

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

View File

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

View File

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

View File

@ -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<IntegerEvalValue>(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<IntegerEvalValue>(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 //
/////////////////

View File

@ -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<EvalValue> EvaluateExpression(const BoundExpression *expression);
const shared_ptr<NumericEvalValue> EvaluateIntegerExpression(const BoundExpression *expression);

View File

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

View File

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

View File

@ -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<TokenKind> &endTokens, const vector<const ParsedStatement *> &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:

View File

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