diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index ae5a077..54b1a51 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -46,6 +46,8 @@ namespace Porygon::Binder { return this->BindConditionalStatement(statement); case ParsedStatementKind::NumericalFor: return this->BindNumericalForStatement(statement); + case ParsedStatementKind::GenericFor: + return this -> BindGenericForStatement(statement); case ParsedStatementKind::Bad: return new BoundBadStatement(); } @@ -105,7 +107,7 @@ namespace Porygon::Binder { return new BoundIndexAssignmentStatement(indexable, valueExpression); } - std::shared_ptr ParseTypeIdentifier(HashedString s) { + std::shared_ptr ParseTypeIdentifier(const HashedString& s) { auto hash = s.GetHash(); switch (hash) { case HashedString::ConstHash("number"): @@ -253,6 +255,46 @@ namespace Porygon::Binder { return new BoundNumericalForStatement(variableKey.GetKey(), start, end, step, block); } + BoundStatement *Binder::BindGenericForStatement(const ParsedStatement *statement) { + auto genericFor = (ParsedGenericForStatement*) statement; + auto boundIterator = BindExpression(genericFor -> GetIteratorExpression()); + const auto& itType = boundIterator -> GetType(); + if (!itType -> CanBeIterated()){ + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::CantIterateExpression, statement->GetStartPosition(), + statement->GetLength()); + return new BoundBadStatement(); + } + auto keyType = itType -> GetIteratorKeyType(); + auto keyIdentifier = genericFor -> GetKeyIdentifier(); + this -> _scope -> GoInnerScope(); + auto keyVariableAssignment = this -> _scope -> CreateExplicitLocal(keyIdentifier, keyType); + if (keyVariableAssignment.GetResult() != VariableAssignmentResult::Ok){ + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::CantAssignVariable, statement->GetStartPosition(), + statement->GetLength()); + return new BoundBadStatement(); + } + auto keyVariable = keyVariableAssignment.GetKey(); + + auto valueIdentifier = genericFor -> GetValueIdentifier(); + auto isValueVariableDefined = valueIdentifier.GetHash() != 0; + BoundVariableKey* valueVariable = nullptr; + if (isValueVariableDefined){ + auto valueType = itType -> GetIndexedType(keyType.get()); + auto valueVariableAssignment = this -> _scope -> CreateExplicitLocal(valueIdentifier, valueType); + if (valueVariableAssignment.GetResult() != VariableAssignmentResult::Ok){ + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::CantAssignVariable, statement->GetStartPosition(), + statement->GetLength()); + return new BoundBadStatement(); + } + valueVariable = valueVariableAssignment.GetKey(); + } + auto boundBlock = this -> BindBlockStatement(genericFor -> GetBlock()); + + this -> _scope -> GoOuterScope(); + + return new BoundGenericForStatement(keyVariable, valueVariable, boundIterator, boundBlock); + } + ///////////////// // Expressions // ///////////////// @@ -629,5 +671,4 @@ namespace Porygon::Binder { return new BoundTableExpression((BoundBlockStatement *) block, tableType, expression->GetStartPosition(), expression->GetLength()); } - } diff --git a/src/Binder/Binder.hpp b/src/Binder/Binder.hpp index b7335bb..4cbb366 100644 --- a/src/Binder/Binder.hpp +++ b/src/Binder/Binder.hpp @@ -29,6 +29,7 @@ namespace Porygon::Binder { BoundStatement *BindReturnStatement(const ParsedStatement *statement); BoundStatement *BindConditionalStatement(const ParsedStatement *statement); BoundStatement *BindNumericalForStatement(const ParsedStatement *statement); + BoundStatement *BindGenericForStatement(const ParsedStatement *statement); // Expressions diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index 8ed9f54..62fe4a6 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -21,6 +21,7 @@ namespace Porygon::Binder { Return, Conditional, NumericalFor, + GenericFor, }; class BoundStatement { @@ -247,6 +248,48 @@ namespace Porygon::Binder { return _block; } }; + + class BoundGenericForStatement : public BoundStatement { + const BoundVariableKey* _keyIdentifier; + const BoundVariableKey* _valueIdentifier; + const BoundExpression* _iterator; + + const BoundStatement *_block; + public: + explicit BoundGenericForStatement(const BoundVariableKey *keyIdentifier, + const BoundVariableKey *valueIdentifier, + const BoundExpression *iterator, const BoundStatement *block) + : _keyIdentifier(keyIdentifier), _valueIdentifier(valueIdentifier), _iterator(iterator), _block(block) { + } + + ~BoundGenericForStatement() final { + delete _keyIdentifier; + delete _valueIdentifier; + delete _iterator; + delete _block; + } + + const BoundStatementKind GetKind() const final { + return BoundStatementKind::GenericFor; + } + + const BoundVariableKey* GetKeyIdentifier() const{ + return _keyIdentifier; + } + + const BoundVariableKey* GetValueIdentifier() const{ + return _valueIdentifier; + } + + const BoundExpression* GetIterator() const{ + return _iterator; + } + + const BoundStatement* GetBlock() const{ + return _block; + } + }; + } diff --git a/src/Diagnostics/DiagnosticCode.hpp b/src/Diagnostics/DiagnosticCode.hpp index b3cad74..53534d6 100644 --- a/src/Diagnostics/DiagnosticCode.hpp +++ b/src/Diagnostics/DiagnosticCode.hpp @@ -26,7 +26,8 @@ namespace Porygon::Diagnostics { InvalidTypeName, UserDataFieldNoGetter, UserDataFieldNoSetter, - NumericalForArgumentNotANumber + NumericalForArgumentNotANumber, + CantIterateExpression }; } #endif //PORYGONLANG_DIAGNOSTICCODE_HPP diff --git a/src/Diagnostics/DiagnosticsHolder.cpp b/src/Diagnostics/DiagnosticsHolder.cpp index bf5603c..991711f 100644 --- a/src/Diagnostics/DiagnosticsHolder.cpp +++ b/src/Diagnostics/DiagnosticsHolder.cpp @@ -69,6 +69,7 @@ std::string SeverityToString(DiagnosticSeverity s){ case DiagnosticSeverity::Info: return "Info"; case DiagnosticSeverity::Warning: return "Warning"; case DiagnosticSeverity::Error: return "Error"; + default: throw; } } diff --git a/src/Evaluator/EvalValues/EvalValue.hpp b/src/Evaluator/EvalValues/EvalValue.hpp index e7c71b8..3f2d7b6 100644 --- a/src/Evaluator/EvalValues/EvalValue.hpp +++ b/src/Evaluator/EvalValues/EvalValue.hpp @@ -2,11 +2,17 @@ #ifndef PORYGONLANG_EVALVALUE_HPP #define PORYGONLANG_EVALVALUE_HPP -#include "../../ScriptType.hpp" -#include "../EvaluationException.hpp" #include #include #include +#include "../../ScriptType.hpp" +#include "../EvaluationException.hpp" +namespace Porygon::Evaluation{ + class EvalValue; + class Iterator; +} +#include "../Iterator/Iterator.hpp" + namespace Porygon::Evaluation { class EvalValue { @@ -54,6 +60,10 @@ namespace Porygon::Evaluation { virtual void SetIndexValue(EvalValue *key, const shared_ptr &value) const { throw EvaluationException("Can't index this EvalValue"); } + + virtual Iterator * GetKeyIterator() const{ + throw EvaluationException("Can't iterate over this EvalValue"); + } }; class BooleanEvalValue : public EvalValue { diff --git a/src/Evaluator/EvalValues/TableEvalValue.cpp b/src/Evaluator/EvalValues/TableEvalValue.cpp new file mode 100644 index 0000000..d40ae2f --- /dev/null +++ b/src/Evaluator/EvalValues/TableEvalValue.cpp @@ -0,0 +1,6 @@ +#include "TableEvalValue.hpp" +#include "../Iterator/SimpleKeyIterator.hpp" + +Porygon::Evaluation::Iterator * Porygon::Evaluation::TableEvalValue::GetKeyIterator() const { + return new TableKeyIterator(this); +} diff --git a/src/Evaluator/EvalValues/TableEvalValue.hpp b/src/Evaluator/EvalValues/TableEvalValue.hpp index 755e185..6ae8134 100644 --- a/src/Evaluator/EvalValues/TableEvalValue.hpp +++ b/src/Evaluator/EvalValues/TableEvalValue.hpp @@ -1,3 +1,4 @@ + #ifndef PORYGONLANG_TABLEEVALVALUE_HPP #define PORYGONLANG_TABLEEVALVALUE_HPP #include @@ -8,18 +9,20 @@ using namespace std; namespace Porygon::Evaluation { class TableEvalValue : public EvalValue { - shared_ptr>> _table; - size_t _hash; + const shared_ptr>> _table; + const size_t _hash; - explicit TableEvalValue(shared_ptr>> table, size_t hash) { - _table = std::move(table); - _hash = hash; + explicit TableEvalValue(shared_ptr>> table, size_t hash) + : _table(std::move(table)), + _hash(hash) + { } public: - explicit TableEvalValue(shared_ptr>> table) { - _table = std::move(table); - _hash = rand(); + explicit TableEvalValue(shared_ptr>> table) : + _table(std::move(table)), + _hash(rand()) + { } const TypeClass GetTypeClass() const final { @@ -57,6 +60,8 @@ namespace Porygon::Evaluation { this->_table->at(Utilities::HashedString::CreateLookup(hash)) = value; } + Iterator * GetKeyIterator() const final; + const _Rb_tree_const_iterator>> GetTableIterator() const{ return _table->cbegin(); }; diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index c111952..120f26f 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -45,6 +45,8 @@ namespace Porygon::Evaluation { return this->EvaluateConditionalStatement((BoundConditionalStatement *) statement); case BoundStatementKind::NumericalFor: return this->EvaluateNumericalForStatement((BoundNumericalForStatement*)statement); + case BoundStatementKind::GenericFor: + return this-> EvaluateGenericForStatement((BoundGenericForStatement*)statement); case BoundStatementKind::Bad: throw; @@ -151,6 +153,32 @@ namespace Porygon::Evaluation { } } + void Evaluator::EvaluateGenericForStatement(const BoundGenericForStatement *statement) { + auto iteratorVal = EvaluateExpression(statement -> GetIterator()); + auto iterator = iteratorVal -> GetKeyIterator(); + auto keyVariable = statement ->GetKeyIdentifier(); + auto valueVariable = statement ->GetValueIdentifier(); + + this -> _evaluationScope -> CreateVariable(keyVariable, nullptr); + if (valueVariable != nullptr) + this -> _evaluationScope -> CreateVariable(valueVariable, nullptr); + auto block = (BoundBlockStatement*)statement -> GetBlock(); + + while (iterator->MoveNext()){ + auto currentKey = iterator->GetCurrent(); + this -> _evaluationScope -> SetVariable(keyVariable, currentKey); + if (valueVariable != nullptr){ + auto currentValue = iteratorVal -> IndexValue(currentKey.get()); + this -> _evaluationScope -> SetVariable(valueVariable, currentValue); + } + this -> EvaluateBlockStatement(block); + } + } + + ///////////////// + // Expressions // + ///////////////// + const shared_ptr Evaluator::EvaluateExpression(const BoundExpression *expression) { auto type = expression->GetType(); switch (type->GetClass()) { @@ -425,5 +453,4 @@ namespace Porygon::Evaluation { throw; } } - } \ No newline at end of file diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index 237f06f..5ab4d9c 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -32,6 +32,7 @@ namespace Porygon::Evaluation{ void EvaluateReturnStatement(const BoundReturnStatement *statement); void EvaluateConditionalStatement(const BoundConditionalStatement *statement); void EvaluateNumericalForStatement(const BoundNumericalForStatement *statement); + void EvaluateGenericForStatement(const BoundGenericForStatement *statement); const shared_ptr EvaluateExpression(const BoundExpression *expression); const shared_ptr EvaluateIntegerExpression(const BoundExpression *expression); diff --git a/src/Evaluator/Iterator/Iterator.hpp b/src/Evaluator/Iterator/Iterator.hpp index 4f5e381..72a2d6c 100644 --- a/src/Evaluator/Iterator/Iterator.hpp +++ b/src/Evaluator/Iterator/Iterator.hpp @@ -4,8 +4,6 @@ #include #include "../EvalValues/EvalValue.hpp" -#include "../EvalValues/TableEvalValue.hpp" -#include "../EvalValues/StringEvalValue.hpp" using namespace std; @@ -17,26 +15,6 @@ namespace Porygon::Evaluation{ virtual void Reset() = 0; }; - class TableKeyIterator : Iterator{ - _Rb_tree_const_iterator>> _iterator; - _Rb_tree_const_iterator>> _end; - public: - TableKeyIterator(shared_ptr table) - : _iterator(table->GetTableIterator()), _end(table->GetTableIteratorEnd()){} - - shared_ptr GetCurrent() final{ - return make_shared(*_iterator->first.GetString()); - } - - bool MoveNext() final{ - std::advance(_iterator, 1); - return _iterator != _end; - } - - void Reset(){ - throw EvaluationException("Can't reset table key iterator"); - } - }; } diff --git a/src/Evaluator/Iterator/SimpleKeyIterator.cpp b/src/Evaluator/Iterator/SimpleKeyIterator.cpp new file mode 100644 index 0000000..3615c5f --- /dev/null +++ b/src/Evaluator/Iterator/SimpleKeyIterator.cpp @@ -0,0 +1,3 @@ + + +#include "SimpleKeyIterator.hpp" diff --git a/src/Evaluator/Iterator/SimpleKeyIterator.hpp b/src/Evaluator/Iterator/SimpleKeyIterator.hpp new file mode 100644 index 0000000..d855b21 --- /dev/null +++ b/src/Evaluator/Iterator/SimpleKeyIterator.hpp @@ -0,0 +1,39 @@ + +#ifndef PORYGONLANG_SIMPLEKEYITERATOR_HPP +#define PORYGONLANG_SIMPLEKEYITERATOR_HPP + +#include "Iterator.hpp" +#include "../EvalValues/TableEvalValue.hpp" +#include "../EvalValues/StringEvalValue.hpp" + +namespace Porygon::Evaluation{ + class TableKeyIterator : public Iterator{ + _Rb_tree_const_iterator>> _iterator; + _Rb_tree_const_iterator>> _end; + bool _hasStarted = false; + public: + explicit TableKeyIterator(const TableEvalValue* table) + : _iterator(table->GetTableIterator()), _end(table->GetTableIteratorEnd()){} + + shared_ptr GetCurrent() final{ + return make_shared(*_iterator->first.GetString()); + } + + bool MoveNext() final{ + if (_hasStarted){ + std::advance(_iterator, 1); + } else{ + _hasStarted = true; + } + return _iterator != _end; + } + + void Reset(){ + throw EvaluationException("Can't reset table key iterator"); + } + }; +} + + + +#endif //PORYGONLANG_SIMPLEKEYITERATOR_HPP diff --git a/src/Parser/ParsedStatements/ParsedStatement.hpp b/src/Parser/ParsedStatements/ParsedStatement.hpp index c3be5cf..574c7cd 100644 --- a/src/Parser/ParsedStatements/ParsedStatement.hpp +++ b/src/Parser/ParsedStatements/ParsedStatement.hpp @@ -21,7 +21,8 @@ namespace Porygon::Parser { FunctionDeclaration, Return, Conditional, - NumericalFor + NumericalFor, + GenericFor }; class ParsedStatement { @@ -129,7 +130,7 @@ namespace Porygon::Parser { const vector _parameters; const ParsedBlockStatement *_block; public: - ParsedFunctionDeclarationStatement(HashedString identifier, vector parameters, + ParsedFunctionDeclarationStatement(const HashedString& identifier, vector parameters, ParsedBlockStatement *block, unsigned int start, unsigned int length) : ParsedStatement(start, length), _identifier(identifier), _parameters(std::move(parameters)), @@ -164,7 +165,7 @@ namespace Porygon::Parser { const HashedString _identifier; const ParsedExpression *_expression; public: - ParsedAssignmentStatement(bool local, const HashedString identifier, ParsedExpression *expression, + ParsedAssignmentStatement(bool local, const HashedString& identifier, ParsedExpression *expression, unsigned int start, unsigned int length) : ParsedStatement(start, length), _local(local), _identifier(identifier), _expression(expression) { } @@ -288,7 +289,7 @@ namespace Porygon::Parser { const ParsedExpression *_step; const ParsedStatement *_block; public: - ParsedNumericalForStatement(const HashedString identifier, const ParsedExpression *start, + ParsedNumericalForStatement(const HashedString& identifier, const ParsedExpression *start, const ParsedExpression *end, const ParsedExpression *step, const ParsedStatement *block, unsigned int startPos, unsigned int length) : ParsedStatement(startPos, length), _identifier(identifier), _start(start), _end(end), _step(step), _block(block) { @@ -326,5 +327,43 @@ namespace Porygon::Parser { } }; + class ParsedGenericForStatement : public ParsedStatement{ + const HashedString _keyIdentifier; + const HashedString _valueIdentifier; + const ParsedExpression* _iteratorExpression; + const ParsedStatement* _block; + public: + ParsedGenericForStatement(const HashedString& keyIdentifier, const HashedString& valueIdentifier, + const ParsedExpression *iteratorExpression, const ParsedStatement *block, + unsigned int start, unsigned int length) + : ParsedStatement(start, length), _keyIdentifier(keyIdentifier), _valueIdentifier(valueIdentifier), + _iteratorExpression(iteratorExpression), _block(block) {} + + ~ParsedGenericForStatement() final{ + delete _iteratorExpression; + delete _block; + } + + const ParsedStatementKind GetKind() const final { + return ParsedStatementKind::GenericFor; + } + + const HashedString GetKeyIdentifier() const{ + return _keyIdentifier; + } + + const HashedString GetValueIdentifier() const{ + return _valueIdentifier; + } + + const ParsedExpression* GetIteratorExpression() const{ + return _iteratorExpression; + } + + const ParsedStatement* GetBlock() const{ + return _block; + } + }; + } #endif //PORYGONLANG_PARSEDSTATEMENT_HPP diff --git a/src/Parser/Parser.cpp b/src/Parser/Parser.cpp index 640a77d..dc99c93 100644 --- a/src/Parser/Parser.cpp +++ b/src/Parser/Parser.cpp @@ -267,7 +267,47 @@ namespace Porygon::Parser { } ParsedStatement *Parser::ParseGenericForStatement(const IToken *current) { - return nullptr; + auto keyIdentifier = ((IdentifierToken*) current)->GetValue(); + IdentifierToken* valueIdentifierToken = nullptr; + bool hasErrors = false; + auto next = this -> Next(); + if (next -> GetKind() == TokenKind::CommaToken){ + next = this -> Next(); + if (next->GetKind() != TokenKind::Identifier){ + hasErrors = true; + this->ScriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::UnexpectedToken, next->GetStartPosition(), + next->GetLength()); + } else{ + valueIdentifierToken = (IdentifierToken*) next; + next = this -> Next(); + } + } + if (next->GetKind() != TokenKind::InKeyword && !hasErrors){ + hasErrors = true; + this->ScriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::UnexpectedToken, next->GetStartPosition(), + next->GetLength()); + } + auto expression = this -> ParseExpression(this -> Next()); + next = this -> Next(); + if (next -> GetKind() != TokenKind::DoKeyword && !hasErrors){ + hasErrors = true; + this->ScriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::UnexpectedToken, next->GetStartPosition(), + next->GetLength()); + } + auto block = this -> ParseBlock({TokenKind ::EndKeyword}); + auto startPos = current->GetStartPosition(); + if (hasErrors){ + return new ParsedBadStatement(startPos, block -> GetEndPosition() - startPos); + } else{ + auto valueIdentifier = HashedString::CreateLookup(0); + if (valueIdentifierToken != nullptr){ + return new ParsedGenericForStatement(keyIdentifier, valueIdentifierToken -> GetValue(), expression, block, + startPos, block -> GetEndPosition() - startPos); + } else{ + return new ParsedGenericForStatement(keyIdentifier, HashedString::CreateLookup(0), expression, block, + startPos, block -> GetEndPosition() - startPos); + } + } } ///////////////// diff --git a/src/ScriptType.hpp b/src/ScriptType.hpp index dd77c41..59f3241 100644 --- a/src/ScriptType.hpp +++ b/src/ScriptType.hpp @@ -61,7 +61,14 @@ namespace Porygon{ virtual const shared_ptr GetIndexedType(ScriptType* indexer) const; virtual const shared_ptr GetIndexedType(uint32_t hash) const{ - throw "Shouldn't be possible"; + throw "This type told the binder it can be indexed, but it does not implement the resulting type."; + } + + virtual const bool CanBeIterated() const{ + return false; + } + virtual shared_ptr GetIteratorKeyType() const{ + throw "This type told the binder it can be iterated, but it does not implement the resulting type."; } }; @@ -161,12 +168,22 @@ namespace Porygon{ } const bool CanBeIndexedWith(ScriptType* indexer) const final{ - return indexer->GetClass() == TypeClass ::Number; + if (indexer -> GetClass() != TypeClass::Number) + return false; + auto num =(NumericScriptType*)indexer; + return !(num->IsAwareOfFloat() && num->IsFloat()); } const shared_ptr GetIndexedType(ScriptType* indexer) const final{ return _valueType; } + + const bool CanBeIterated() const final{ + return true; + } + shared_ptr GetIteratorKeyType() const final{ + return make_shared(false, 0); + } }; } diff --git a/src/TableScriptType.hpp b/src/TableScriptType.hpp index 77fde1c..5719565 100644 --- a/src/TableScriptType.hpp +++ b/src/TableScriptType.hpp @@ -49,7 +49,6 @@ namespace Porygon{ const int GetLocalVariableCount() const{ return _localVariableCount; } - }; } diff --git a/tests/integration/LoopTests.cpp b/tests/integration/LoopTests.cpp index c056952..82dd0a0 100644 --- a/tests/integration/LoopTests.cpp +++ b/tests/integration/LoopTests.cpp @@ -60,5 +60,35 @@ end delete script; } +TEST_CASE( "Generic for loop over simple numerical table, get keys", "[integration]" ) { + auto script = Script::Create(uR"( +local table = {1, 3, 5, 7, 9} +result = "" +for i in table do + result = result + i +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateString() == u"12345"); + delete script; +} + +TEST_CASE( "Generic for loop over simple numerical table, get values", "[integration]" ) { + auto script = Script::Create(uR"( +local table = {1, 3, 5, 7, 9} +result = "" +for i,v in table do + result = result + v +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto var = script->GetVariable(u"result"); + REQUIRE(var->EvaluateString() == u"13579"); + delete script; +} + #endif