From 641b6784c79bd27c63d7631d3b7736b9e6a18585 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sun, 11 Aug 2019 12:32:18 +0200 Subject: [PATCH] Support for loading variables from module with require --- src/Binder/Binder.cpp | 10 ++--- src/Binder/BoundVariables/BoundScope.cpp | 7 +++- src/Binder/BoundVariables/BoundScope.hpp | 5 ++- src/Evaluator/Evaluator.cpp | 5 ++- src/Script.cpp | 22 +++++++---- src/Script.hpp | 4 ++ src/Utilities/HashedString.hpp | 15 +++++++- tests/integration/ModuleTests.cpp | 49 +++++++++++++++++++----- 8 files changed, 89 insertions(+), 28 deletions(-) diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index c368635..bdb1149 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -630,12 +630,12 @@ namespace Porygon::Binder { exp->GetLength()); return new BoundBadExpression(exp->GetStartPosition(), exp ->GetLength()); } - auto module = opt->ResolveModule(transformedKey); + auto module = Script::Clone(opt->ResolveModule(transformedKey)); if (module -> GetReturnType() == nullptr){ for (const auto& v: *module->GetScriptVariables()){ - //TODO: Currently a hack, will always make all variables nil - auto type = make_shared(TypeClass::Nil); - this -> _scope -> AssignVariable(v.first, type); + auto type = module->GetVariableType(v.first); + auto result = this -> _scope -> AssignVariable(v.first, type); + delete result.GetKey(); } } @@ -737,7 +737,7 @@ namespace Porygon::Binder { BoundExpression *Binder::BindTableExpression(const ParsedTableExpression *expression) { auto tableScope = new map(); - auto innerScope = new BoundScope(tableScope); + auto innerScope = new BoundScope(tableScope, nullptr); auto currentScope = this->_scope; this->_scope = innerScope; auto block = dynamic_cast(this->BindBlockStatement(expression->GetBlock())); diff --git a/src/Binder/BoundVariables/BoundScope.cpp b/src/Binder/BoundVariables/BoundScope.cpp index ab82c82..38c3cf4 100644 --- a/src/Binder/BoundVariables/BoundScope.cpp +++ b/src/Binder/BoundVariables/BoundScope.cpp @@ -3,8 +3,10 @@ #include "../../StandardLibraries/StaticScope.hpp" namespace Porygon::Binder { - BoundScope::BoundScope(map *tableScope) { + BoundScope::BoundScope(map *tableScope, + unordered_map>* tableVariableTypes) { _tableScope = tableScope; + _tableVariableTypes = tableVariableTypes; _currentScope = 1; _lastCreatedScope = 1; auto localUpmostScope = new map(); @@ -92,6 +94,9 @@ namespace Porygon::Binder { if (exists < 0) { // Creation _tableScope->insert({identifier, new BoundVariable(type)}); + if (_tableVariableTypes != nullptr){ + _tableVariableTypes->insert({identifier, type}); + } return VariableAssignment(VariableAssignmentResult::Ok, new BoundVariableKey(identifier, 0, true)); } else { // Assigning diff --git a/src/Binder/BoundVariables/BoundScope.hpp b/src/Binder/BoundVariables/BoundScope.hpp index 1e4b7eb..33917e8 100644 --- a/src/Binder/BoundVariables/BoundScope.hpp +++ b/src/Binder/BoundVariables/BoundScope.hpp @@ -4,6 +4,7 @@ #include #include +#include #include #include #include "BoundVariable.hpp" @@ -16,11 +17,13 @@ using namespace std; namespace Porygon::Binder { class BoundScope { map *_tableScope; + unordered_map> * _tableVariableTypes; vector *> _localScope; int _currentScope; int _lastCreatedScope; public: - explicit BoundScope(map *tableScope); + explicit BoundScope(map *tableScope, + unordered_map>* tableVariableTypes); ~BoundScope(); diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index dfbaf99..a908dc4 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -402,13 +402,14 @@ namespace Porygon::Evaluation { EvalValuePointer Evaluator::EvaluateRequireExpression(const BoundExpression* expression) { auto module = dynamic_cast(expression)->GetModule(); + auto result = module->Evaluate(); if (module ->GetReturnType() == nullptr){ for (const auto& v: *module->GetScriptVariables()){ - this->_scriptVariables->insert({v.first, v.second.Clone()}); + this->_scriptVariables->at(v.first) = v.second.Clone(); } return nullptr; } else{ - return module -> Evaluate().Take(); + return result.Take(); } } } \ No newline at end of file diff --git a/src/Script.cpp b/src/Script.cpp index c42cba2..e62031a 100644 --- a/src/Script.cpp +++ b/src/Script.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include "Script.hpp" @@ -26,6 +25,7 @@ Porygon::Script::Script(const u16string& s) : Diagnostics(make_shared(s)), _boundScript(nullptr), _scriptVariables(new map()), + _scriptTypes(new unordered_map>{}), _scriptOptions(nullptr) { _evaluator = new Evaluator(this -> _scriptVariables, this -> GetScriptOptions()); @@ -40,6 +40,8 @@ Porygon::Script::~Script() { delete this -> _evaluator; this->_scriptVariables->clear(); delete this->_scriptVariables; + this->_scriptTypes->clear(); + delete this->_scriptTypes; } void Porygon::Script::Parse(const u16string& script) { @@ -53,13 +55,18 @@ void Porygon::Script::Parse(const u16string& script) { lexResult.clear(); if (!Diagnostics->HasErrors()){ map scriptScope; - auto bindScope = new BoundScope(&scriptScope); + unordered_map> variableTypes; + auto bindScope = new BoundScope(&scriptScope, &variableTypes); this->_boundScript = shared_ptr(Binder::Binder::Bind(this, parseResult, bindScope)); for (const auto& v : scriptScope){ this->_scriptVariables -> insert({v.first, nullptr}); delete v.second; } + for (const auto& v : variableTypes){ + this->_scriptTypes -> insert({v.first, nullptr}); + } scriptScope.clear(); + variableTypes.clear(); } delete parseResult; } @@ -73,10 +80,6 @@ bool Porygon::Script::HasVariable(const u16string &key) { return f != _scriptVariables->end(); } -/*const EvalValue *Porygon::Script::GetLastValue() { - return _evaluator->GetLastValue(); -}*/ - bool Porygon::Script::HasFunction(const u16string &key) { auto f = _scriptVariables->find(HashedString::CreateLookup(key)); return f != _scriptVariables->end() && f.operator->()->second->GetTypeClass() == TypeClass ::Function; @@ -89,9 +92,13 @@ const EvalValue* Porygon::Script::CallFunction(const u16string &key, const vecto Porygon::Script *Porygon::Script::Clone(const Porygon::Script *script) { auto s = new Script(script->_boundScript, script->Diagnostics); - for (auto v: *script->_scriptVariables){ + for (const auto& v: *script->_scriptVariables){ s->_scriptVariables->insert({v.first, nullptr}); } + for (const auto& v: *script->_scriptTypes){ + s->_scriptTypes->insert({v.first, v.second}); + } + s->_returnType = script->_returnType; return s; } @@ -101,6 +108,7 @@ Porygon::Script::Script(shared_ptr boundScript, : _boundScript(std::move(boundScript)), Diagnostics(std::move(diagnostics)), _scriptVariables(new map()), + _scriptTypes(new unordered_map>{}), _scriptOptions(nullptr) { _evaluator = new Evaluator(_scriptVariables, this -> GetScriptOptions()); diff --git a/src/Script.hpp b/src/Script.hpp index b1fffc4..9f66cce 100644 --- a/src/Script.hpp +++ b/src/Script.hpp @@ -19,6 +19,7 @@ namespace Porygon{ class Script { Evaluator* _evaluator; map* _scriptVariables; + unordered_map>* _scriptTypes; shared_ptr _boundScript; shared_ptr _returnType = nullptr; ScriptOptions* _scriptOptions; @@ -54,6 +55,9 @@ namespace Porygon{ const EvalValue* GetVariable(const u16string& key); bool HasVariable(const u16string& key); + shared_ptr GetVariableType(const Utilities::HashedString& key){ + return _scriptTypes->at(key); + } const EvalValue* CallFunction(const u16string& key, const vector& variables); bool HasFunction(const u16string& key); diff --git a/src/Utilities/HashedString.hpp b/src/Utilities/HashedString.hpp index 5bb0f4a..1d0ea28 100644 --- a/src/Utilities/HashedString.hpp +++ b/src/Utilities/HashedString.hpp @@ -63,9 +63,20 @@ namespace Porygon::Utilities{ inline bool operator>(const HashedString& b) const{ return _hash > b._hash; } - - + std::size_t operator()(const HashedString& k) const { + return _hash; + } }; } +namespace std { + template <> + struct hash { + std::size_t operator()(const Porygon::Utilities::HashedString& k) const{ + return k.GetHash(); + } + }; +}; + + #endif //PORYGONLANG_HASHEDSTRING_HPP diff --git a/tests/integration/ModuleTests.cpp b/tests/integration/ModuleTests.cpp index 522f454..d71baa6 100644 --- a/tests/integration/ModuleTests.cpp +++ b/tests/integration/ModuleTests.cpp @@ -4,20 +4,32 @@ using namespace Porygon; class ModuleHandler{ - static unordered_map MODULES; + class Internal{ + public: + unordered_map MODULES; - ~ModuleHandler(){ - for (auto v: MODULES){ - delete v.second; + Internal(){ + MODULES = { + {"simple_return", Script::Create(u"return 500")}, + {"simple_variables", Script::Create(u"foo = 50\nbar = \'test\'")} + }; } - } + + ~Internal(){ + for (const auto& v: MODULES){ + delete v.second; + } + MODULES.clear(); + } + }; + static Internal _internal; inline static bool DoesModuleExist(const string& moduleName){ - return MODULES.find(moduleName) != MODULES.end(); + return _internal.MODULES.find(moduleName) != _internal.MODULES.end(); } inline static Script* ResolveModule(const string& moduleName){ - return MODULES[moduleName]; + return _internal.MODULES[moduleName]; } public: @@ -27,9 +39,7 @@ public: } }; -unordered_map ModuleHandler::MODULES = unordered_map{ - {"simple_return", Script::Create(u"return 500")} -}; +ModuleHandler::Internal ModuleHandler::_internal; TEST_CASE( "Require simple return script", "[integration]" ) { ModuleHandler::Initialize(); @@ -42,4 +52,23 @@ return require("simple_return") delete script; } +TEST_CASE( "Require simple variables script", "[integration]" ) { + ModuleHandler::Initialize(); + auto script = Script::Create(uR"( +require("simple_variables") +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + REQUIRE(script->HasVariable(u"foo")); + REQUIRE(script->HasVariable(u"bar")); + auto foo = script->GetVariable(u"foo"); + auto bar = script->GetVariable(u"bar"); + CHECK(foo->EvaluateInteger() == 50); + CHECK(bar->EvaluateString() == u"test"); + + delete foo; + delete bar; + delete script; +} + #endif