diff --git a/src/ScriptTypes/FunctionScriptType.hpp b/src/ScriptTypes/FunctionScriptType.hpp index 5604c01..03afd9b 100644 --- a/src/ScriptTypes/FunctionScriptType.hpp +++ b/src/ScriptTypes/FunctionScriptType.hpp @@ -65,6 +65,13 @@ namespace Porygon { this -> RegisterFunctionOption(option); }; + explicit GenericFunctionScriptType(const vector& options) + : ScriptType(Porygon::TypeClass::Function){ + for (auto option: options){ + this -> RegisterFunctionOption(option); + } + }; + ~GenericFunctionScriptType() final{ for (auto o: *_options){ delete o; @@ -72,9 +79,10 @@ namespace Porygon { delete _options; } - void RegisterFunctionOption (GenericFunctionOption * opt) const{ + const GenericFunctionScriptType* RegisterFunctionOption (GenericFunctionOption * opt) const{ opt->SetOption(_options->size()); _options->push_back(opt); + return this; } GenericFunctionOption* GetFunctionOption(const vector>& parameters) const{ diff --git a/src/StandardLibraries/BasicLibrary.hpp b/src/StandardLibraries/BasicLibrary.hpp index d6173a1..cc91eb2 100644 --- a/src/StandardLibraries/BasicLibrary.hpp +++ b/src/StandardLibraries/BasicLibrary.hpp @@ -46,7 +46,8 @@ namespace Porygon::StandardLibraries{ throw Evaluation::EvaluationException(conv); } static shared_ptr GetErrorFuncType(){ - return GetFuncType(make_shared(TypeClass::Nil), {{make_shared(false, 0)}}); + return GetFuncType(make_shared(TypeClass::Nil), + {{make_shared(false, 0)}}); } //endregion //region Print @@ -206,7 +207,7 @@ namespace Porygon::StandardLibraries{ values->insert({typeLookup, typeFunc}); // Register IsFloat function - auto isFloatFuncType = BasicLibrary::GetTypeFuncType(); + auto isFloatFuncType = BasicLibrary::GetIsFloatFuncType(); auto isFloatLookup = Utilities::HashedString::CreateLookup(u"isfloat"); auto isFloatFunc = BasicLibrary::GetFuncEvalValue(_isFloat, isFloatFuncType, 1); bound->insert({isFloatLookup, new Binder::BoundVariable(isFloatFuncType)}); diff --git a/src/StandardLibraries/MathLibrary.hpp b/src/StandardLibraries/MathLibrary.hpp new file mode 100644 index 0000000..d195be1 --- /dev/null +++ b/src/StandardLibraries/MathLibrary.hpp @@ -0,0 +1,63 @@ +#ifndef PORYGONLANG_MATHLIBRARY_HPP +#define PORYGONLANG_MATHLIBRARY_HPP + +#include + +#include "../Evaluator/EvalValues/EvalValue.hpp" +#include "../Evaluator/EvalValues/NumericEvalValue.hpp" + +#include "../UserData/UserDataScriptType.hpp" +#include "../ScriptTypes/FunctionScriptType.hpp" +#include "../ScriptTypes/ScriptType.hpp" +#include "../UserData/UserDataFunctionType.hpp" +#include "../Evaluator/EvalValues/ScriptFunctionEvalValue.hpp" +#include "../UserData/UserDataFunction.hpp" +#include "../Utilities/Random.hpp" + +namespace Porygon::StandardLibraries { + using namespace Porygon::Evaluation; + using namespace Porygon::Utilities; + class MathLibrary { + + //region templates + +#define FLOAT_TYPE new NumericScriptType(true, true) +#define INTEGER_TYPE new NumericScriptType(true, false) +#define FUNCTION(fieldName) \ + [](void* obj) -> const Porygon::Evaluation::EvalValue* { \ + auto t = new Porygon::Evaluation::GenericFunctionEvalValue(make_shared(), \ + Porygon::Utilities::Random::Get()); \ + t->RegisterOption(new Porygon::UserData::UserDataFunction(fieldName, nullptr)); \ + return t;}, \ + nullptr + //endregion + + static const EvalValue* _abs(void*, const ScriptOptions*, const Evaluation::EvalValue* parameters[], int parameterCount){ + auto parameter = dynamic_cast(parameters[0]); + if (parameter->IsFloat()){ + return new FloatEvalValue(std::abs(parameter->EvaluateFloat())); + } else{ + return new IntegerEvalValue(std::abs(parameter->EvaluateInteger())); + } + } + + static UserData::UserDataFunctionOption* CreateFunctionOption(ScriptType* returnType, std::vector params){ + return UserData::UserDataFunctionOption::FromRawPointers(returnType, std::move(params)); + } + + + public: + static UserData::UserData* CreateUserData(){ + return new UserData::UserData({ + {HashedString::ConstHash("abs"), + new UserData::UserDataField( + (new GenericFunctionScriptType()) + ->RegisterFunctionOption(CreateFunctionOption(INTEGER_TYPE, {INTEGER_TYPE})) + ->RegisterFunctionOption(CreateFunctionOption(FLOAT_TYPE, {FLOAT_TYPE})) + , FUNCTION(_abs))} + }); + } + }; +} + +#endif //PORYGONLANG_MATHLIBRARY_HPP diff --git a/src/StandardLibraries/StaticScope.cpp b/src/StandardLibraries/StaticScope.cpp index b882c6b..8d5216d 100644 --- a/src/StandardLibraries/StaticScope.cpp +++ b/src/StandardLibraries/StaticScope.cpp @@ -1,3 +1,3 @@ #include "StaticScope.hpp" -Porygon::StandardLibraries::StaticScope::InternalScope Porygon::StandardLibraries::StaticScope::_internal; \ No newline at end of file +Porygon::StandardLibraries::StaticScope::InternalScope* Porygon::StandardLibraries::StaticScope::_internal = nullptr; \ No newline at end of file diff --git a/src/StandardLibraries/StaticScope.hpp b/src/StandardLibraries/StaticScope.hpp index 75cbd34..e5febea 100644 --- a/src/StandardLibraries/StaticScope.hpp +++ b/src/StandardLibraries/StaticScope.hpp @@ -1,13 +1,16 @@ -#include #ifndef PORYGONLANG_STATICSCOPE_HPP #define PORYGONLANG_STATICSCOPE_HPP #include +#include +#include "BasicLibrary.hpp" +#include "MathLibrary.hpp" #include "../Utilities/HashedString.hpp" #include "../Binder/BoundVariables/BoundVariable.hpp" #include "../Evaluator/EvalValues/EvalValue.hpp" -#include "BasicLibrary.hpp" +#include "../UserData/UserDataStorage.hpp" +#include "../UserData/UserDataValue.hpp" using namespace std; namespace Porygon::StandardLibraries{ @@ -23,6 +26,11 @@ namespace Porygon::StandardLibraries{ InternalScope(){ BasicLibrary::RegisterVariables(&_boundVariables, &_variables); + auto mathData = MathLibrary::CreateUserData(); + UserData::UserDataStorage::RegisterType(Utilities::HashedString::ConstHash("__math__"), mathData); + auto mathHash = Utilities::HashedString(new u16string(u"math")); + _boundVariables.insert({mathHash, new Binder::BoundVariable(make_shared(mathData))}); + _variables.insert({mathHash, new UserData::UserDataValue(mathData, nullptr)}); } ~InternalScope(){ @@ -35,23 +43,35 @@ namespace Porygon::StandardLibraries{ } }; - static InternalScope _internal; + static InternalScope* _internal; + + inline static InternalScope* GetScope(){ + if (!_internal) + _internal = new InternalScope(); + return _internal; + } public: + + ~StaticScope(){ + delete _internal; + } + + static void RegisterVariable(const Utilities::HashedString& identifier, shared_ptr type, Evaluation::EvalValue* value){ - _internal._boundVariables.insert({identifier, new Binder::BoundVariable(std::move(type))}); - _internal._variables.insert({identifier, value}); + GetScope()->_boundVariables.insert({identifier, new Binder::BoundVariable(std::move(type))}); + GetScope()->_variables.insert({identifier, value}); } static Binder::BoundVariable* GetBoundVariable(const Utilities::HashedString &identifier){ - auto found = _internal._boundVariables.find(identifier); - if (found != _internal._boundVariables.end()) { + auto found = GetScope()->_boundVariables.find(identifier); + if (found != GetScope()->_boundVariables.end()) { return found->second; } return nullptr; } inline static Evaluation::EvalValuePointer GetVariable(const Utilities::HashedString &identifier){ - return _internal._variables[identifier]->Clone(); + return GetScope()->_variables[identifier]->Clone(); } }; } diff --git a/src/UserData/UserData.hpp b/src/UserData/UserData.hpp index 326c83a..9da5e97 100644 --- a/src/UserData/UserData.hpp +++ b/src/UserData/UserData.hpp @@ -8,7 +8,7 @@ namespace Porygon::UserData { class UserData { - std::unordered_map _fields; + std::unordered_map> _fields; // Binary operations UserDataBinaryOperation* _addition = nullptr; @@ -27,14 +27,15 @@ namespace Porygon::UserData { public: explicit UserData(std::unordered_map fields) - : _fields(std::move(fields)) { + for (auto f: fields){ + _fields.insert({f.first, unique_ptr(f.second)}); + } } ~UserData(){ - for (auto f: _fields){ - delete f.second; - } + _fields.clear(); + delete _addition; delete _subtraction; delete _multiplication; @@ -57,11 +58,11 @@ namespace Porygon::UserData { [[nodiscard]] inline UserDataField *GetField(uint32_t fieldId) const { - return _fields.at(fieldId); + return _fields.at(fieldId).get(); } inline void CreateField(uint32_t fieldId, UserDataField *field) { - _fields.insert({fieldId, field}); + _fields.insert({fieldId, unique_ptr(field)}); } [[nodiscard]] diff --git a/src/UserData/UserDataStorage.cpp b/src/UserData/UserDataStorage.cpp index a52cfb8..5a6a91b 100644 --- a/src/UserData/UserDataStorage.cpp +++ b/src/UserData/UserDataStorage.cpp @@ -2,6 +2,6 @@ #include "UserDataStorage.hpp" namespace Porygon::UserData { - UserDataStorage::_internalDataStorage UserDataStorage::_internal = UserDataStorage::_internalDataStorage(); + UserDataStorage::_internalDataStorage UserDataStorage::_internal; std::mutex UserDataStorage::_userDataMutex; } \ No newline at end of file diff --git a/src/UserData/UserDataStorage.hpp b/src/UserData/UserDataStorage.hpp index 605125a..1945d41 100644 --- a/src/UserData/UserDataStorage.hpp +++ b/src/UserData/UserDataStorage.hpp @@ -11,16 +11,17 @@ namespace Porygon::UserData { private: class _internalDataStorage { public: - std::unordered_map _userData; + std::unordered_map> _userData; + + _internalDataStorage() + : _userData(std::unordered_map>(0)) + { + } ~_internalDataStorage(){ - for (auto u: _userData){ - delete u.second; - } _userData.clear(); } - _internalDataStorage() = default; _internalDataStorage( const UserDataStorage& ) = delete; // non construction-copyable _internalDataStorage& operator=( const UserDataStorage& ) = delete; // non copyable @@ -28,19 +29,14 @@ namespace Porygon::UserData { static _internalDataStorage _internal; static std::mutex _userDataMutex; - public: static void RegisterType(uint32_t i, UserData *ud) { std::lock_guard guard(_userDataMutex); - UserDataStorage::_internal._userData.insert({i, ud}); + UserDataStorage::_internal._userData.insert({i, std::unique_ptr(ud)}); } - static void ClearTypes(){ - std::lock_guard guard(_userDataMutex); - for (auto u: _internal._userData){ - delete u.second; - } - _internal._userData.clear(); + static void RemoveType(uint32_t key){ + _internal._userData.erase(key); } inline static bool HasUserDataType(uint32_t i) { @@ -48,7 +44,7 @@ namespace Porygon::UserData { } inline static UserData* GetUserDataType(uint32_t i) { - return UserDataStorage::_internal._userData[i]; + return UserDataStorage::_internal._userData.at(i).get(); } }; } diff --git a/tests/integration/ModuleTests.cpp b/tests/integration/ModuleTests.cpp index d71baa6..6a9739d 100644 --- a/tests/integration/ModuleTests.cpp +++ b/tests/integration/ModuleTests.cpp @@ -22,14 +22,20 @@ class ModuleHandler{ MODULES.clear(); } }; - static Internal _internal; + static Internal* _internal; + + static Internal* GetInternal(){ + if (!_internal) + _internal = new Internal(); + return _internal; + } inline static bool DoesModuleExist(const string& moduleName){ - return _internal.MODULES.find(moduleName) != _internal.MODULES.end(); + return GetInternal()->MODULES.find(moduleName) != GetInternal()->MODULES.end(); } inline static Script* ResolveModule(const string& moduleName){ - return _internal.MODULES[moduleName]; + return GetInternal()->MODULES[moduleName]; } public: @@ -39,7 +45,7 @@ public: } }; -ModuleHandler::Internal ModuleHandler::_internal; +ModuleHandler::Internal* ModuleHandler::_internal = nullptr; TEST_CASE( "Require simple return script", "[integration]" ) { ModuleHandler::Initialize(); diff --git a/tests/integration/UserDataTests.cpp b/tests/integration/UserDataTests.cpp index 6a84ee0..2531e6d 100644 --- a/tests/integration/UserDataTests.cpp +++ b/tests/integration/UserDataTests.cpp @@ -65,7 +65,7 @@ end delete parameter; delete script; delete variable; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Sets UserData value", "[integration]" ) { @@ -84,7 +84,7 @@ end REQUIRE(obj->foo == 5000); delete obj; delete parameter; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Calls UserData function", "[integration]" ) { @@ -104,7 +104,7 @@ end delete obj; delete parameter; delete result; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Calls UserData function with parameters", "[integration]" ) { @@ -124,7 +124,7 @@ end delete obj; delete parameter; delete result; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Gets userdata vector value", "[integration]" ) { @@ -148,7 +148,7 @@ end delete result; delete script; delete func; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Sets userdata vector value", "[integration]" ) { @@ -170,7 +170,7 @@ end delete parameter; delete script; delete func; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Iterate over userdata vector keys", "[integration]" ) { @@ -197,7 +197,7 @@ end delete script; delete func; delete result; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } TEST_CASE( "Iterate over userdata vector values", "[integration]" ) { @@ -224,7 +224,7 @@ end delete script; delete func; delete result; - UserDataStorage::ClearTypes(); + UserDataStorage::RemoveType(HashedString::ConstHash("testObject")); } #endif diff --git a/tests/standardLibraries/MatLibrary.cpp b/tests/standardLibraries/MatLibrary.cpp new file mode 100644 index 0000000..ad73b1f --- /dev/null +++ b/tests/standardLibraries/MatLibrary.cpp @@ -0,0 +1,26 @@ +#ifdef TESTS_BUILD +#include +#include "../src/Script.hpp" +#include "../../src/ScriptOptions.hpp" +#include + +using namespace Porygon; + +TEST_CASE( "Abs positive returns positive", "[integration]" ) { + Script* script = Script::Create(u"return math.abs(684)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + auto result = script -> Evaluate(); + CHECK(result->EvaluateInteger() == 684); + delete script; +} + +TEST_CASE( "Abs negative returns positive", "[integration]" ) { + Script* script = Script::Create(u"return math.abs(-684)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + auto result = script -> Evaluate(); + CHECK(result->EvaluateInteger() == 684); + delete script; +} + +#endif +