diff --git a/src/UserData/UserDataCollectionType.cpp b/src/UserData/UserDataCollectionType.cpp new file mode 100644 index 0000000..88d562f --- /dev/null +++ b/src/UserData/UserDataCollectionType.cpp @@ -0,0 +1 @@ +#include "UserDataCollectionType.hpp" diff --git a/src/UserData/UserDataCollectionType.hpp b/src/UserData/UserDataCollectionType.hpp new file mode 100644 index 0000000..789a7a5 --- /dev/null +++ b/src/UserData/UserDataCollectionType.hpp @@ -0,0 +1,61 @@ +#ifndef PORYGONLANG_USERDATACOLLECTIONTYPE_HPP +#define PORYGONLANG_USERDATACOLLECTIONTYPE_HPP + +#include +#include "../ScriptTypes/ScriptType.hpp" + +namespace Porygon::UserData { + class UserDataCollectionType : public ScriptType { + shared_ptr _keyType; + shared_ptr _valueType; + const bool _indexable; + const bool _iterable; + public: + UserDataCollectionType(shared_ptr keyType, shared_ptr valueType, + bool indexable, bool iterable) + : ScriptType(TypeClass::Table), + _keyType(std::move(keyType)), + _valueType(std::move(valueType)), + _indexable(indexable), + _iterable(iterable) + {} + + static shared_ptr CreateIndexable(const shared_ptr& keyType, const shared_ptr& valueType){ + return make_shared(keyType, valueType, true, true); + } + + static UserDataCollectionType* CreateIndexable(const ScriptType* keyType, const ScriptType* valueType){ + return new UserDataCollectionType(shared_ptr(keyType), + shared_ptr(valueType), true, true); + } + + static shared_ptr CreateIterable(const shared_ptr& valueType){ + return make_shared(nullptr, valueType, false, true); + } + + bool CanBeIndexedWith(const ScriptType* indexer) const final{ + if (!_indexable){ + return false; + } + return indexer->operator==(_keyType.get()); + } + + [[nodiscard]] shared_ptr GetIndexedType(const ScriptType* indexer) const final{ + return _valueType; + } + + [[nodiscard]] bool CanBeIterated() const final{ + return _iterable; + } + + [[nodiscard]] shared_ptr GetIteratorKeyType() const final{ + if (_indexable){ + return _keyType; + } else{ + return _valueType; + } + } + }; +} + +#endif //PORYGONLANG_USERDATACOLLECTIONTYPE_HPP diff --git a/src/UserData/UserDataCollectionValue.cpp b/src/UserData/UserDataCollectionValue.cpp new file mode 100644 index 0000000..3aa1873 --- /dev/null +++ b/src/UserData/UserDataCollectionValue.cpp @@ -0,0 +1 @@ +#include "UserDataCollectionValue.hpp" diff --git a/src/UserData/UserDataCollectionValue.hpp b/src/UserData/UserDataCollectionValue.hpp new file mode 100644 index 0000000..8728036 --- /dev/null +++ b/src/UserData/UserDataCollectionValue.hpp @@ -0,0 +1,75 @@ +#ifndef PORYGONLANG_USERDATACOLLECTIONVALUE_HPP +#define PORYGONLANG_USERDATACOLLECTIONVALUE_HPP + +#include + +#include "UserDataCollectionType.hpp" +#include "../Evaluator/EvalValues/EvalValue.hpp" +#include "../Utilities/Random.hpp" + +namespace Porygon::UserData { + class UserDataCollectionHelper{ + void* _parentObject; + const EvalValue* (*_get)(void*, const EvalValue*); + void (*_set)(void*, const EvalValue* , const EvalValue*); + + public: + UserDataCollectionHelper(void* parentObject, + const EvalValue* (*get)(void*, const EvalValue*), + void (*set)(void*, const EvalValue*, const EvalValue*)) + : _parentObject(parentObject), _get(get), _set(set){} + + const EvalValue* Get(const EvalValue* key) const{ + return _get(_parentObject, key); + } + + void Set(const EvalValue* key, const EvalValue* value) const{ + _set(_parentObject, key, value); + } + }; + + class UserDataCollectionValue : public Evaluation::EvalValue{ + shared_ptr _type; + shared_ptr _helper; + const size_t _hash; + UserDataCollectionValue(shared_ptr type, + shared_ptr helper, size_t hash) + : _type(std::move(type)), _helper(std::move(helper)), _hash(hash) + { + } + public: + + UserDataCollectionValue(ScriptType* type, const UserDataCollectionHelper* helper) + : _type((UserDataCollectionType*)type), _helper(helper), _hash(Utilities::Random::Get()) + { + } + + + [[nodiscard]] TypeClass GetTypeClass() const final{ + return TypeClass ::Table; + } + + bool operator==(const EvalValue *b) const final{ + return b->GetHashCode() == _hash; + } + + [[nodiscard]] EvalValue* Clone() const final{ + return new UserDataCollectionValue(_type, _helper, _hash); + } + + [[nodiscard]] std::size_t GetHashCode() const final{ + return _hash; + } + + const EvalValue* IndexValue(const EvalValue *val) const final{ + return _helper->Get(val); + } + + void SetIndexValue(const EvalValue *key, const EvalValue* value) const final{ + _helper->Set(key, value); + } + }; +} + + +#endif //PORYGONLANG_USERDATACOLLECTIONVALUE_HPP diff --git a/src/UserData/UserDataTemplates.hpp b/src/UserData/UserDataTemplates.hpp index 6ec7894..6b0d887 100644 --- a/src/UserData/UserDataTemplates.hpp +++ b/src/UserData/UserDataTemplates.hpp @@ -28,6 +28,8 @@ #define PORYGON_INTEGER_TYPE ((Porygon::ScriptType*)new Porygon::NumericScriptType(true, false)) #define PORYGON_FLOAT_TYPE ((Porygon::ScriptType*)new Porygon::NumericScriptType(true, true)) #define PORYGON_STRING_TYPE ((Porygon::ScriptType*)new Porygon::StringScriptType(false, 0)) +#define PORYGON_INDEXABLE_TYPE(keyType, valueType) \ + ((Porygon::ScriptType*)Porygon::UserData::UserDataCollectionType::CreateIndexable(keyType, valueType)) #define PORYGON_FIELD(fieldName, fieldType, getterHelper, setterHelper) \ { \ @@ -42,7 +44,7 @@ { \ Porygon::Utilities::HashedString::ConstHash(#fieldName), \ new Porygon::UserData::UserDataField(fieldType, \ - [](void* obj) -> Porygon::Evaluation::EvalValue* { return new getterHelper;}, \ + [](void* obj) -> const Porygon::Evaluation::EvalValue* { return new getterHelper;}, \ nullptr \ ) \ }, \ @@ -63,6 +65,30 @@ PORYGON_READONLY_FIELD(fieldName, PORYGON_FLOAT_TYPE, \ Porygon::EvaluationFloatEvalValue(((T_USERDATA*)obj)->fieldName)) +/* +#define PORYGON_INDEXABLE_FIELD(fieldName, keyType, valueType) \ + PORYGON_FIELD(fieldName, PORYGON_INDEXABLE_TYPE(keyType, valueType), \ + const Porygon::Evaluation::IntegerEvalValue(((T_USERDATA*)obj)->fieldName), val->EvaluateInteger()) +*/ + +#define PORYGON_READONLY_VECTOR_FIELD(fieldName, valueType) \ + PORYGON_READONLY_FIELD(fieldName, PORYGON_INDEXABLE_TYPE(PORYGON_INTEGER_TYPE, valueType), \ + Porygon::UserData::UserDataCollectionValue( \ + PORYGON_INDEXABLE_TYPE(PORYGON_INTEGER_TYPE, valueType), \ + new UserDataCollectionHelper( \ + obj, \ + [](void* obj, const EvalValue* v) -> const EvalValue*{ \ + auto index = v->EvaluateInteger() - 1; \ + auto val = ((T_USERDATA*)obj)->fieldName;\ + return EvalValueHelper::Create(val[index]); \ + } \ + , [](void* obj, const EvalValue* key, const EvalValue* value){ \ + auto index = key->EvaluateInteger() - 1;\ + ((T_USERDATA*)obj)->fieldName[index] = value->EvaluateInteger(); \ + }) \ + ) \ + ) + #define PORYGON_FUNCTION(fieldName, returnType, ...) \ { \ @@ -73,7 +99,8 @@ \ \ [](void* obj) -> const Porygon::Evaluation::EvalValue* { \ - auto t = new Porygon::Evaluation::GenericFunctionEvalValue(make_shared(), rand()); \ + auto t = new Porygon::Evaluation::GenericFunctionEvalValue(make_shared(), \ + Porygon::Utilities::Random::Get()); \ t->RegisterOption(new Porygon::UserData::UserDataFunction( \ [](void* obj, const Porygon::Evaluation::EvalValue* par[], int parameterCount) \ -> const Porygon::Evaluation::EvalValue*{return ((const T_USERDATA*)obj)->invoke__##fieldName(obj, par, parameterCount);}, \ diff --git a/tests/integration/UserDataTests.cpp b/tests/integration/UserDataTests.cpp index 917c120..21f5478 100644 --- a/tests/integration/UserDataTests.cpp +++ b/tests/integration/UserDataTests.cpp @@ -8,7 +8,10 @@ #include "../../src/UserData/UserDataFunction.hpp" #include "../../src/UserData/UserDataFunctionType.hpp" #include "../../src/UserData/UserDataTemplates.hpp" +#include "../../src/UserData/UserDataCollectionType.hpp" +#include "../../src/UserData/UserDataCollectionValue.hpp" #include "../../src/Evaluator/EvalValues/EvalValueHelper.hpp" +#include "../../src/Utilities/Random.hpp" using namespace Porygon; using namespace Porygon::UserData; @@ -17,6 +20,7 @@ using namespace Porygon::Utilities; class UserDataTestObject{ public: int foo = 10; + vector fooVector = {5,10,15,25}; int getFoo(){ return foo; } @@ -29,11 +33,17 @@ public: private: PORYGON_PREPARE_FUNCTION(UserDataTestObject, getFoo, IntegerEvalValue) PORYGON_PREPARE_FUNCTION(UserDataTestObject, Addition, IntegerEvalValue, (par[0] -> EvaluateInteger()), (par[1] -> EvaluateInteger())) + void __setFooVector(const EvalValue* key, const EvalValue* value){ + auto index = key->EvaluateInteger(); + fooVector[index] = value->EvaluateInteger(); + } + public: PORYGON_USERDATA(UserDataTestObject, PORYGON_INTEGER_FIELD(foo) PORYGON_INTEGER_FUNCTION(getFoo) PORYGON_INTEGER_FUNCTION(Addition, PORYGON_INTEGER_TYPE, PORYGON_INTEGER_TYPE) + PORYGON_READONLY_VECTOR_FIELD(fooVector, PORYGON_INTEGER_TYPE) ) }; @@ -117,5 +127,51 @@ end UserDataStorage::ClearTypes(); } +TEST_CASE( "Gets userdata vector value", "[integration]" ) { + UserDataStorage::RegisterType(HashedString::ConstHash("testObject"), UserDataTestObject::__createUserData()); + Script* script = Script::Create(R"( +function testFunc(testObject obj) + return obj.fooVector[1] +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto func = (GenericFunctionEvalValue*)script -> GetVariable(u"testFunc"); + auto funcType = func -> GetType(); + REQUIRE(funcType->GetFirstOption()->GetReturnType()->GetClass() == TypeClass::Number); + auto obj = new UserDataTestObject(); + auto parameter = new UserDataValue(HashedString::ConstHash("testObject"), obj); + auto result = script->CallFunction(u"testFunc", {parameter}); + REQUIRE(result -> EvaluateInteger() == 5); + delete obj; + delete parameter; + delete result; + delete script; + delete func; + UserDataStorage::ClearTypes(); +} + +TEST_CASE( "Sets userdata vector value", "[integration]" ) { + UserDataStorage::RegisterType(HashedString::ConstHash("testObject"), UserDataTestObject::__createUserData()); + Script* script = Script::Create(R"( +function testFunc(testObject obj) + obj.fooVector[3] = 684 +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto func = (GenericFunctionEvalValue*)script -> GetVariable(u"testFunc"); + auto funcType = func -> GetType(); + auto obj = new UserDataTestObject(); + auto parameter = new UserDataValue(HashedString::ConstHash("testObject"), obj); + script->CallFunction(u"testFunc", {parameter}); + REQUIRE(obj->fooVector[2] == 684); + delete obj; + delete parameter; + delete script; + delete func; + UserDataStorage::ClearTypes(); +} + #endif