Implements initial math library, several reworks for Userdata memory management
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2019-08-17 14:42:48 +02:00
parent 13b382def2
commit 5e96250d96
Signed by: Deukhoofd
GPG Key ID: ADF2E9256009EDCE
11 changed files with 167 additions and 46 deletions

View File

@ -65,6 +65,13 @@ namespace Porygon {
this -> RegisterFunctionOption(option); this -> RegisterFunctionOption(option);
}; };
explicit GenericFunctionScriptType(const vector<GenericFunctionOption*>& options)
: ScriptType(Porygon::TypeClass::Function){
for (auto option: options){
this -> RegisterFunctionOption(option);
}
};
~GenericFunctionScriptType() final{ ~GenericFunctionScriptType() final{
for (auto o: *_options){ for (auto o: *_options){
delete o; delete o;
@ -72,9 +79,10 @@ namespace Porygon {
delete _options; delete _options;
} }
void RegisterFunctionOption (GenericFunctionOption * opt) const{ const GenericFunctionScriptType* RegisterFunctionOption (GenericFunctionOption * opt) const{
opt->SetOption(_options->size()); opt->SetOption(_options->size());
_options->push_back(opt); _options->push_back(opt);
return this;
} }
GenericFunctionOption* GetFunctionOption(const vector<shared_ptr<const ScriptType>>& parameters) const{ GenericFunctionOption* GetFunctionOption(const vector<shared_ptr<const ScriptType>>& parameters) const{

View File

@ -46,7 +46,8 @@ namespace Porygon::StandardLibraries{
throw Evaluation::EvaluationException(conv); throw Evaluation::EvaluationException(conv);
} }
static shared_ptr<GenericFunctionScriptType> GetErrorFuncType(){ static shared_ptr<GenericFunctionScriptType> GetErrorFuncType(){
return GetFuncType(make_shared<ScriptType>(TypeClass::Nil), {{make_shared<StringScriptType>(false, 0)}}); return GetFuncType(make_shared<ScriptType>(TypeClass::Nil),
{{make_shared<StringScriptType>(false, 0)}});
} }
//endregion //endregion
//region Print //region Print
@ -206,7 +207,7 @@ namespace Porygon::StandardLibraries{
values->insert({typeLookup, typeFunc}); values->insert({typeLookup, typeFunc});
// Register IsFloat function // Register IsFloat function
auto isFloatFuncType = BasicLibrary::GetTypeFuncType(); auto isFloatFuncType = BasicLibrary::GetIsFloatFuncType();
auto isFloatLookup = Utilities::HashedString::CreateLookup(u"isfloat"); auto isFloatLookup = Utilities::HashedString::CreateLookup(u"isfloat");
auto isFloatFunc = BasicLibrary::GetFuncEvalValue(_isFloat, isFloatFuncType, 1); auto isFloatFunc = BasicLibrary::GetFuncEvalValue(_isFloat, isFloatFuncType, 1);
bound->insert({isFloatLookup, new Binder::BoundVariable(isFloatFuncType)}); bound->insert({isFloatLookup, new Binder::BoundVariable(isFloatFuncType)});

View File

@ -0,0 +1,63 @@
#ifndef PORYGONLANG_MATHLIBRARY_HPP
#define PORYGONLANG_MATHLIBRARY_HPP
#include <utility>
#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<GenericFunctionScriptType>(), \
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<const NumericEvalValue*>(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<ScriptType*> 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

View File

@ -1,3 +1,3 @@
#include "StaticScope.hpp" #include "StaticScope.hpp"
Porygon::StandardLibraries::StaticScope::InternalScope Porygon::StandardLibraries::StaticScope::_internal; Porygon::StandardLibraries::StaticScope::InternalScope* Porygon::StandardLibraries::StaticScope::_internal = nullptr;

View File

@ -1,13 +1,16 @@
#include <utility>
#ifndef PORYGONLANG_STATICSCOPE_HPP #ifndef PORYGONLANG_STATICSCOPE_HPP
#define PORYGONLANG_STATICSCOPE_HPP #define PORYGONLANG_STATICSCOPE_HPP
#include <map> #include <map>
#include <utility>
#include "BasicLibrary.hpp"
#include "MathLibrary.hpp"
#include "../Utilities/HashedString.hpp" #include "../Utilities/HashedString.hpp"
#include "../Binder/BoundVariables/BoundVariable.hpp" #include "../Binder/BoundVariables/BoundVariable.hpp"
#include "../Evaluator/EvalValues/EvalValue.hpp" #include "../Evaluator/EvalValues/EvalValue.hpp"
#include "BasicLibrary.hpp" #include "../UserData/UserDataStorage.hpp"
#include "../UserData/UserDataValue.hpp"
using namespace std; using namespace std;
namespace Porygon::StandardLibraries{ namespace Porygon::StandardLibraries{
@ -23,6 +26,11 @@ namespace Porygon::StandardLibraries{
InternalScope(){ InternalScope(){
BasicLibrary::RegisterVariables(&_boundVariables, &_variables); 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<UserData::UserDataScriptType>(mathData))});
_variables.insert({mathHash, new UserData::UserDataValue(mathData, nullptr)});
} }
~InternalScope(){ ~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: public:
~StaticScope(){
delete _internal;
}
static void RegisterVariable(const Utilities::HashedString& identifier, shared_ptr<ScriptType> type, Evaluation::EvalValue* value){ static void RegisterVariable(const Utilities::HashedString& identifier, shared_ptr<ScriptType> type, Evaluation::EvalValue* value){
_internal._boundVariables.insert({identifier, new Binder::BoundVariable(std::move(type))}); GetScope()->_boundVariables.insert({identifier, new Binder::BoundVariable(std::move(type))});
_internal._variables.insert({identifier, value}); GetScope()->_variables.insert({identifier, value});
} }
static Binder::BoundVariable* GetBoundVariable(const Utilities::HashedString &identifier){ static Binder::BoundVariable* GetBoundVariable(const Utilities::HashedString &identifier){
auto found = _internal._boundVariables.find(identifier); auto found = GetScope()->_boundVariables.find(identifier);
if (found != _internal._boundVariables.end()) { if (found != GetScope()->_boundVariables.end()) {
return found->second; return found->second;
} }
return nullptr; return nullptr;
} }
inline static Evaluation::EvalValuePointer GetVariable(const Utilities::HashedString &identifier){ inline static Evaluation::EvalValuePointer GetVariable(const Utilities::HashedString &identifier){
return _internal._variables[identifier]->Clone(); return GetScope()->_variables[identifier]->Clone();
} }
}; };
} }

View File

@ -8,7 +8,7 @@
namespace Porygon::UserData { namespace Porygon::UserData {
class UserData { class UserData {
std::unordered_map<uint32_t, UserDataField *> _fields; std::unordered_map<uint32_t, unique_ptr<UserDataField>> _fields;
// Binary operations // Binary operations
UserDataBinaryOperation* _addition = nullptr; UserDataBinaryOperation* _addition = nullptr;
@ -27,14 +27,15 @@ namespace Porygon::UserData {
public: public:
explicit UserData(std::unordered_map<uint32_t, UserDataField *> fields) explicit UserData(std::unordered_map<uint32_t, UserDataField *> fields)
: _fields(std::move(fields))
{ {
for (auto f: fields){
_fields.insert({f.first, unique_ptr<UserDataField>(f.second)});
}
} }
~UserData(){ ~UserData(){
for (auto f: _fields){ _fields.clear();
delete f.second;
}
delete _addition; delete _addition;
delete _subtraction; delete _subtraction;
delete _multiplication; delete _multiplication;
@ -57,11 +58,11 @@ namespace Porygon::UserData {
[[nodiscard]] [[nodiscard]]
inline UserDataField *GetField(uint32_t fieldId) const { inline UserDataField *GetField(uint32_t fieldId) const {
return _fields.at(fieldId); return _fields.at(fieldId).get();
} }
inline void CreateField(uint32_t fieldId, UserDataField *field) { inline void CreateField(uint32_t fieldId, UserDataField *field) {
_fields.insert({fieldId, field}); _fields.insert({fieldId, unique_ptr<UserDataField>(field)});
} }
[[nodiscard]] [[nodiscard]]

View File

@ -2,6 +2,6 @@
#include "UserDataStorage.hpp" #include "UserDataStorage.hpp"
namespace Porygon::UserData { namespace Porygon::UserData {
UserDataStorage::_internalDataStorage UserDataStorage::_internal = UserDataStorage::_internalDataStorage(); UserDataStorage::_internalDataStorage UserDataStorage::_internal;
std::mutex UserDataStorage::_userDataMutex; std::mutex UserDataStorage::_userDataMutex;
} }

View File

@ -11,16 +11,17 @@ namespace Porygon::UserData {
private: private:
class _internalDataStorage { class _internalDataStorage {
public: public:
std::unordered_map<uint32_t, UserData*> _userData; std::unordered_map<uint32_t, std::unique_ptr<UserData>> _userData;
_internalDataStorage()
: _userData(std::unordered_map<uint32_t, std::unique_ptr<UserData>>(0))
{
}
~_internalDataStorage(){ ~_internalDataStorage(){
for (auto u: _userData){
delete u.second;
}
_userData.clear(); _userData.clear();
} }
_internalDataStorage() = default;
_internalDataStorage( const UserDataStorage& ) = delete; // non construction-copyable _internalDataStorage( const UserDataStorage& ) = delete; // non construction-copyable
_internalDataStorage& operator=( const UserDataStorage& ) = delete; // non copyable _internalDataStorage& operator=( const UserDataStorage& ) = delete; // non copyable
@ -28,19 +29,14 @@ namespace Porygon::UserData {
static _internalDataStorage _internal; static _internalDataStorage _internal;
static std::mutex _userDataMutex; static std::mutex _userDataMutex;
public: public:
static void RegisterType(uint32_t i, UserData *ud) { static void RegisterType(uint32_t i, UserData *ud) {
std::lock_guard<std::mutex> guard(_userDataMutex); std::lock_guard<std::mutex> guard(_userDataMutex);
UserDataStorage::_internal._userData.insert({i, ud}); UserDataStorage::_internal._userData.insert({i, std::unique_ptr<UserData>(ud)});
} }
static void ClearTypes(){ static void RemoveType(uint32_t key){
std::lock_guard<std::mutex> guard(_userDataMutex); _internal._userData.erase(key);
for (auto u: _internal._userData){
delete u.second;
}
_internal._userData.clear();
} }
inline static bool HasUserDataType(uint32_t i) { inline static bool HasUserDataType(uint32_t i) {
@ -48,7 +44,7 @@ namespace Porygon::UserData {
} }
inline static UserData* GetUserDataType(uint32_t i) { inline static UserData* GetUserDataType(uint32_t i) {
return UserDataStorage::_internal._userData[i]; return UserDataStorage::_internal._userData.at(i).get();
} }
}; };
} }

View File

@ -22,14 +22,20 @@ class ModuleHandler{
MODULES.clear(); 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){ 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){ inline static Script* ResolveModule(const string& moduleName){
return _internal.MODULES[moduleName]; return GetInternal()->MODULES[moduleName];
} }
public: public:
@ -39,7 +45,7 @@ public:
} }
}; };
ModuleHandler::Internal ModuleHandler::_internal; ModuleHandler::Internal* ModuleHandler::_internal = nullptr;
TEST_CASE( "Require simple return script", "[integration]" ) { TEST_CASE( "Require simple return script", "[integration]" ) {
ModuleHandler::Initialize(); ModuleHandler::Initialize();

View File

@ -65,7 +65,7 @@ end
delete parameter; delete parameter;
delete script; delete script;
delete variable; delete variable;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Sets UserData value", "[integration]" ) { TEST_CASE( "Sets UserData value", "[integration]" ) {
@ -84,7 +84,7 @@ end
REQUIRE(obj->foo == 5000); REQUIRE(obj->foo == 5000);
delete obj; delete obj;
delete parameter; delete parameter;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Calls UserData function", "[integration]" ) { TEST_CASE( "Calls UserData function", "[integration]" ) {
@ -104,7 +104,7 @@ end
delete obj; delete obj;
delete parameter; delete parameter;
delete result; delete result;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Calls UserData function with parameters", "[integration]" ) { TEST_CASE( "Calls UserData function with parameters", "[integration]" ) {
@ -124,7 +124,7 @@ end
delete obj; delete obj;
delete parameter; delete parameter;
delete result; delete result;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Gets userdata vector value", "[integration]" ) { TEST_CASE( "Gets userdata vector value", "[integration]" ) {
@ -148,7 +148,7 @@ end
delete result; delete result;
delete script; delete script;
delete func; delete func;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Sets userdata vector value", "[integration]" ) { TEST_CASE( "Sets userdata vector value", "[integration]" ) {
@ -170,7 +170,7 @@ end
delete parameter; delete parameter;
delete script; delete script;
delete func; delete func;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Iterate over userdata vector keys", "[integration]" ) { TEST_CASE( "Iterate over userdata vector keys", "[integration]" ) {
@ -197,7 +197,7 @@ end
delete script; delete script;
delete func; delete func;
delete result; delete result;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
TEST_CASE( "Iterate over userdata vector values", "[integration]" ) { TEST_CASE( "Iterate over userdata vector values", "[integration]" ) {
@ -224,7 +224,7 @@ end
delete script; delete script;
delete func; delete func;
delete result; delete result;
UserDataStorage::ClearTypes(); UserDataStorage::RemoveType(HashedString::ConstHash("testObject"));
} }
#endif #endif

View File

@ -0,0 +1,26 @@
#ifdef TESTS_BUILD
#include <catch.hpp>
#include "../src/Script.hpp"
#include "../../src/ScriptOptions.hpp"
#include <cstring>
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