From 95c322ed2c9487e54134b5b2b87cf03a820be5ae Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Fri, 21 Jun 2019 17:03:13 +0200 Subject: [PATCH] Implements userdata function support --- src/Binder/Binder.cpp | 2 +- .../EvalValues/ScriptFunctionEvalValue.hpp | 76 +++++++++------- src/Evaluator/Evaluator.cpp | 44 ++++++---- src/ScriptType.hpp | 32 +++++-- src/UserData/UserDataFunction.cpp | 2 + src/UserData/UserDataFunction.hpp | 36 ++++++++ src/UserData/UserDataFunctionType.cpp | 2 + src/UserData/UserDataFunctionType.hpp | 22 +++++ tests/integration/UserData.cpp | 86 ++++++++++++++++++- 9 files changed, 245 insertions(+), 57 deletions(-) create mode 100644 src/UserData/UserDataFunction.cpp create mode 100644 src/UserData/UserDataFunction.hpp create mode 100644 src/UserData/UserDataFunctionType.cpp create mode 100644 src/UserData/UserDataFunctionType.hpp diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index d676a20..c54c840 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -453,7 +453,7 @@ namespace Porygon::Binder { expression->GetLength()); return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength()); } - auto functionType = std::dynamic_pointer_cast(type); + auto functionType = std::dynamic_pointer_cast(type); auto parameterTypes = functionType->GetParameterTypes(); auto givenParameters = expression->GetParameters(); if (parameterTypes.size() != givenParameters->size()) { diff --git a/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp b/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp index b788123..8cb3d3b 100644 --- a/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp +++ b/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp @@ -1,5 +1,7 @@ #include +#include + #ifndef PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP #define PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP @@ -11,30 +13,19 @@ #include "../Evaluator.hpp" #include "../EvaluationScope/EvaluationScope.hpp" +using namespace std; + namespace Porygon::Evaluation { - class ScriptFunctionEvalValue : public EvalValue { - const std::shared_ptr _innerBlock; - const std::shared_ptr _type; - const std::shared_ptr _scope; + class GenericFunctionEvalValue : public EvalValue{ + protected: + const std::shared_ptr _type; const std::size_t _hash; - explicit ScriptFunctionEvalValue(std::shared_ptr innerBlock, - std::shared_ptr scope, - std::shared_ptr type, size_t hash) - : _type(std::move(type)), - _innerBlock(std::move(innerBlock)), - _scope(std::move(scope)), - _hash(hash) { - } - public: - explicit ScriptFunctionEvalValue(std::shared_ptr innerBlock, - std::shared_ptr scope, - std::shared_ptr type) - : _type(std::move(type)), - _innerBlock(std::move(innerBlock)), - _scope(std::move(scope)), - _hash(rand()) { + GenericFunctionEvalValue(shared_ptr type, size_t hash) + : _type(move(type)), + _hash(hash){ + } const std::shared_ptr GetType() const { @@ -45,25 +36,48 @@ namespace Porygon::Evaluation { return TypeClass::Function; } - const shared_ptr Clone() const final { - // We don't run make_shared here as it can't call private constructors - return shared_ptr(new ScriptFunctionEvalValue(_innerBlock, _scope, _type, _hash)); - } - - const bool operator==(EvalValue *b) const final { if (b->GetTypeClass() != TypeClass::Function) return false; - return this->_hash == ((ScriptFunctionEvalValue *) b)->_hash; + return this->_hash == ((GenericFunctionEvalValue *) b)->_hash; }; - const std::shared_ptr &GetInnerBlock() const { - return _innerBlock; - } - const std::size_t GetHashCode() const final { return _hash; } + }; + + class ScriptFunctionEvalValue : public GenericFunctionEvalValue { + const std::shared_ptr _innerBlock; + const std::shared_ptr _scope; + + explicit ScriptFunctionEvalValue(shared_ptr innerBlock, + shared_ptr scope, + shared_ptr type, size_t hash) + : GenericFunctionEvalValue(move(type), hash), + _innerBlock(std::move(innerBlock)), + _scope(std::move(scope)){ + } + + public: + explicit ScriptFunctionEvalValue(std::shared_ptr innerBlock, + std::shared_ptr scope, + std::shared_ptr type) + : GenericFunctionEvalValue(move(type), rand()), + _innerBlock(std::move(innerBlock)), + _scope(std::move(scope)){ + } + + + const shared_ptr Clone() const final { + // We don't run make_shared here as it can't call private constructors + return shared_ptr(new ScriptFunctionEvalValue(_innerBlock, _scope, + dynamic_pointer_cast(_type), _hash)); + } + + const std::shared_ptr &GetInnerBlock() const { + return _innerBlock; + } const std::shared_ptr &GetScope() const { return _scope; diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index ac307e9..54957fb 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -9,6 +9,7 @@ #include "EvalValues/TableEvalValue.hpp" #include "../Binder/BoundExpressions/BoundTableExpression.hpp" #include "../TableScriptType.hpp" +#include "../UserData/UserDataFunction.hpp" using namespace std; using namespace Porygon::Binder; @@ -274,7 +275,7 @@ namespace Porygon::Evaluation { const shared_ptr Evaluator::EvaluateFunctionCallExpression(const BoundExpression *expression) { auto functionCall = (BoundFunctionCallExpression *) expression; - auto function = dynamic_pointer_cast( + auto function = dynamic_pointer_cast( this->EvaluateExpression(functionCall->GetFunctionExpression())); auto boundParameters = functionCall->GetParameters(); @@ -283,24 +284,35 @@ namespace Porygon::Evaluation { parameters[i] = this->EvaluateExpression(boundParameters->at(i)); } - auto type = std::dynamic_pointer_cast(function->GetType()); + auto type = std::dynamic_pointer_cast(function->GetType()); auto parameterTypes = type->GetParameterTypes(); - auto parameterKeys = type->GetParameterKeys(); - auto originalScope = this->_evaluationScope; - this->_evaluationScope = function->GetScope(); + if (type -> IsScriptFunction()){ + auto scriptFunctionType = std::dynamic_pointer_cast(type); + auto parameterKeys = scriptFunctionType->GetParameterKeys(); + auto originalScope = this->_evaluationScope; + auto scriptFunction = dynamic_pointer_cast(function); + this->_evaluationScope = scriptFunction->GetScope(); - for (int i = 0; i < parameterTypes.size() && i < parameterKeys.size() && i < parameters.size(); i++) { - auto parameter = parameters[i]; - auto key = parameterKeys.at(i); - this->_evaluationScope->CreateVariable(key.get(), parameter->Clone()); + for (int i = 0; i < parameterTypes.size() && i < parameterKeys.size() && i < parameters.size(); i++) { + auto parameter = parameters[i]; + auto key = parameterKeys.at(i); + this->_evaluationScope->CreateVariable(key.get(), parameter->Clone()); + } + this->EvaluateBlockStatement(scriptFunction->GetInnerBlock().get()); + this->_evaluationScope = originalScope; + + this->_hasReturned = false; + auto r = this->_returnValue; + this->_returnValue = nullptr; + return r; + } else{ + auto userDataFunction = dynamic_pointer_cast(function); + EvalValue* arr[parameters.size()]; + for (int i = 0; i < parameters.size(); i++){ + arr[i] = parameters[i].get(); + } + return shared_ptr(userDataFunction -> Call(arr, parameters.size())); } - this->EvaluateBlockStatement(function->GetInnerBlock().get()); - this->_evaluationScope = originalScope; - - this->_hasReturned = false; - auto r = this->_returnValue; - this->_returnValue = nullptr; - return r; } const shared_ptr Evaluator::EvaluateFunction(const ScriptFunctionEvalValue *function, diff --git a/src/ScriptType.hpp b/src/ScriptType.hpp index 1b8f894..dd77c41 100644 --- a/src/ScriptType.hpp +++ b/src/ScriptType.hpp @@ -1,5 +1,7 @@ #include +#include + #ifndef PORYGONLANG_SCRIPTTYPE_HPP #define PORYGONLANG_SCRIPTTYPE_HPP @@ -101,20 +103,16 @@ namespace Porygon{ } }; - class FunctionScriptType : public ScriptType{ + class GenericFunctionScriptType : public ScriptType{ shared_ptr _returnType; vector> _parameterTypes; - vector> _parameterKeys; - int _scopeIndex; public: - FunctionScriptType(std::shared_ptr returnType, vector> parameterTypes, - vector> parameterKeys, int scopeIndex) - : ScriptType(TypeClass::Function){ + GenericFunctionScriptType(std::shared_ptr returnType, vector> parameterTypes) + : ScriptType(TypeClass::Function){ _returnType = std::move(returnType); _parameterTypes = std::move(parameterTypes); - _parameterKeys = std::move(parameterKeys); - _scopeIndex = scopeIndex; } + const shared_ptr GetReturnType() const{ return _returnType; } @@ -127,6 +125,20 @@ namespace Porygon{ return _parameterTypes; } + virtual const bool IsScriptFunction() const = 0; + }; + + class FunctionScriptType : public GenericFunctionScriptType{ + vector> _parameterKeys; + int _scopeIndex; + public: + FunctionScriptType(std::shared_ptr returnType, vector> parameterTypes, + vector> parameterKeys, int scopeIndex) + : GenericFunctionScriptType(std::move(returnType), parameterTypes){ + _parameterKeys = std::move(parameterKeys); + _scopeIndex = scopeIndex; + } + const vector> GetParameterKeys() const{ return _parameterKeys; } @@ -134,6 +146,10 @@ namespace Porygon{ const int GetScopeIndex() const{ return _scopeIndex; } + + const bool IsScriptFunction() const final{ + return true; + } }; class NumericalTableScriptType : public ScriptType{ diff --git a/src/UserData/UserDataFunction.cpp b/src/UserData/UserDataFunction.cpp new file mode 100644 index 0000000..5162d8b --- /dev/null +++ b/src/UserData/UserDataFunction.cpp @@ -0,0 +1,2 @@ + +#include "UserDataFunction.hpp" diff --git a/src/UserData/UserDataFunction.hpp b/src/UserData/UserDataFunction.hpp new file mode 100644 index 0000000..4cda7cd --- /dev/null +++ b/src/UserData/UserDataFunction.hpp @@ -0,0 +1,36 @@ +#ifndef PORYGONLANG_USERDATAFUNCTION_HPP +#define PORYGONLANG_USERDATAFUNCTION_HPP + +#include +#include "../Evaluator/EvalValues/ScriptFunctionEvalValue.hpp" + +namespace Porygon::UserData{ + class UserDataFunction : public Evaluation::GenericFunctionEvalValue { + Evaluation::EvalValue* (*_call)(void* obj, EvalValue* parameters[], int parameterCount); + void *_obj; + + UserDataFunction(Evaluation::EvalValue* (*call)(void* obj, EvalValue* parameters[], int parameterCount), void* obj, + shared_ptr type, size_t hash) : GenericFunctionEvalValue(std::move(type), hash){ + _call = call; + _obj = obj; + } + public: + UserDataFunction(Evaluation::EvalValue* (*call)(void* obj, EvalValue* parameters[], int parameterCount), void* obj, + shared_ptr type) : GenericFunctionEvalValue(std::move(type), rand()){ + _call = call; + _obj = obj; + } + + EvalValue* Call(EvalValue* parameters[], int parameterCount){ + return _call(_obj, parameters, parameterCount); + } + + const shared_ptr Clone() const final { + // We don't run make_shared here as it can't call private constructors + return shared_ptr(new UserDataFunction(_call, _obj, _type, _hash)); + } + }; +} + + +#endif //PORYGONLANG_USERDATAFUNCTION_HPP diff --git a/src/UserData/UserDataFunctionType.cpp b/src/UserData/UserDataFunctionType.cpp new file mode 100644 index 0000000..1051cfe --- /dev/null +++ b/src/UserData/UserDataFunctionType.cpp @@ -0,0 +1,2 @@ + +#include "UserDataFunctionType.hpp" diff --git a/src/UserData/UserDataFunctionType.hpp b/src/UserData/UserDataFunctionType.hpp new file mode 100644 index 0000000..3919d73 --- /dev/null +++ b/src/UserData/UserDataFunctionType.hpp @@ -0,0 +1,22 @@ +#ifndef PORYGONLANG_USERDATAFUNCTIONTYPE_HPP +#define PORYGONLANG_USERDATAFUNCTIONTYPE_HPP + +#include +#include "../ScriptType.hpp" + +namespace Porygon::UserData{ + class UserDataFunctionType : public GenericFunctionScriptType{ + public: + UserDataFunctionType(std::shared_ptr returnType, vector> parameterTypes) + : GenericFunctionScriptType(std::move(returnType), std::move(parameterTypes)){ + + } + + const bool IsScriptFunction() const final{ + return false; + } + }; +} + + +#endif //PORYGONLANG_USERDATAFUNCTIONTYPE_HPP diff --git a/tests/integration/UserData.cpp b/tests/integration/UserData.cpp index 6b82134..1948385 100644 --- a/tests/integration/UserData.cpp +++ b/tests/integration/UserData.cpp @@ -5,6 +5,9 @@ #include "../../src/UserData/UserData.hpp" #include "../../src/UserData/UserDataStorage.hpp" #include "../../src/UserData/UserDataValue.hpp" +#include "../../src/UserData/UserDataFunction.hpp" +#include "../../src/UserData/UserDataFunctionType.hpp" + using namespace Porygon; using namespace Porygon::UserData; using namespace Porygon::Utilities; @@ -12,7 +15,15 @@ using namespace Porygon::Utilities; class UserDataTestObject{ public: int foo = 10; + int getFoo(){ + return foo; + } + int Addition(int a, int b){ + return a + b; + } + +private: static EvalValue* GetFoo(void* obj){ return new IntegerEvalValue(((UserDataTestObject*)obj)->foo); } @@ -21,16 +32,54 @@ public: ((UserDataTestObject*)obj)->foo = val->EvaluateInteger(); } + static EvalValue* CallFooFunction(void* obj, EvalValue* parameters[], int parameterCount){ + return new IntegerEvalValue(((UserDataTestObject*)obj)->getFoo()); + } + + static EvalValue* GetFooFunction(void* obj){ + return new UserDataFunction(CallFooFunction, obj, + make_shared(make_shared(true, false), vector>(0))); + } + + static EvalValue* CallAddition(void* obj, EvalValue* parameters[], int parameterCount){ + return new IntegerEvalValue(((UserDataTestObject*)obj)->Addition( + parameters[0] -> EvaluateInteger(), + parameters[1] -> EvaluateInteger() + )); + } + + static GenericFunctionScriptType* AdditionFunctionType; + + static EvalValue* GetAdditionFunction(void* obj){ + return new UserDataFunction(CallAddition, obj, shared_ptr(AdditionFunctionType)); + } + +public: static Porygon::UserData::UserData* CreateData(){ return new Porygon::UserData::UserData({ { HashedString::ConstHash("foo"), new UserDataField(new NumericScriptType(true, false), GetFoo, SetFoo) + }, + { + HashedString::ConstHash("getFoo"), + new UserDataField(new UserDataFunctionType(make_shared(true, false), {}), GetFooFunction, nullptr) + }, + { + HashedString::ConstHash("Addition"), + new UserDataField(AdditionFunctionType, GetAdditionFunction, nullptr) } - }); + }); } }; +GenericFunctionScriptType* UserDataTestObject::AdditionFunctionType = + new UserDataFunctionType(make_shared(true, false), + vector>{ + make_shared(true, false), + make_shared(true, false) + }); + TEST_CASE( "Gets UserData value", "[integration]" ) { UserDataStorage::RegisterType(HashedString::ConstHash("testObject"), UserDataTestObject::CreateData()); Script* script = Script::Create(R"( @@ -65,6 +114,41 @@ end delete parameter; } +TEST_CASE( "Calls UserData function", "[integration]" ) { + UserDataStorage::RegisterType(HashedString::ConstHash("testObject"), UserDataTestObject::CreateData()); + Script* script = Script::Create(R"( +function testFunc(testObject obj) + return obj.getFoo() +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto obj = new UserDataTestObject(); + auto parameter = new UserDataValue(HashedString::ConstHash("testObject"), obj); + auto result = script->CallFunction(u"testFunc", {parameter}); + REQUIRE(result -> EvaluateInteger() == 10); + delete script; + delete obj; + delete parameter; +} + +TEST_CASE( "Calls UserData function with parameters", "[integration]" ) { + UserDataStorage::RegisterType(HashedString::ConstHash("testObject"), UserDataTestObject::CreateData()); + Script* script = Script::Create(R"( +function testFunc(testObject obj) + return obj.Addition(5046, 8432) +end +)"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto obj = new UserDataTestObject(); + auto parameter = new UserDataValue(HashedString::ConstHash("testObject"), obj); + auto result = script->CallFunction(u"testFunc", {parameter}); + REQUIRE(result -> EvaluateInteger() == 13478); + delete script; + delete obj; + delete parameter; +} #endif