Implements userdata function support
continuous-integration/drone/push Build is failing Details

This commit is contained in:
Deukhoofd 2019-06-21 17:03:13 +02:00
parent 6f7d319148
commit 95c322ed2c
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
9 changed files with 245 additions and 57 deletions

View File

@ -453,7 +453,7 @@ namespace Porygon::Binder {
expression->GetLength());
return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength());
}
auto functionType = std::dynamic_pointer_cast<FunctionScriptType>(type);
auto functionType = std::dynamic_pointer_cast<GenericFunctionScriptType>(type);
auto parameterTypes = functionType->GetParameterTypes();
auto givenParameters = expression->GetParameters();
if (parameterTypes.size() != givenParameters->size()) {

View File

@ -1,5 +1,7 @@
#include <utility>
#include <utility>
#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<BoundBlockStatement> _innerBlock;
const std::shared_ptr<FunctionScriptType> _type;
const std::shared_ptr<EvaluationScope> _scope;
class GenericFunctionEvalValue : public EvalValue{
protected:
const std::shared_ptr<GenericFunctionScriptType> _type;
const std::size_t _hash;
explicit ScriptFunctionEvalValue(std::shared_ptr<BoundBlockStatement> innerBlock,
std::shared_ptr<EvaluationScope> scope,
std::shared_ptr<FunctionScriptType> type, size_t hash)
: _type(std::move(type)),
_innerBlock(std::move(innerBlock)),
_scope(std::move(scope)),
_hash(hash) {
}
public:
explicit ScriptFunctionEvalValue(std::shared_ptr<BoundBlockStatement> innerBlock,
std::shared_ptr<EvaluationScope> scope,
std::shared_ptr<FunctionScriptType> type)
: _type(std::move(type)),
_innerBlock(std::move(innerBlock)),
_scope(std::move(scope)),
_hash(rand()) {
GenericFunctionEvalValue(shared_ptr<GenericFunctionScriptType> type, size_t hash)
: _type(move(type)),
_hash(hash){
}
const std::shared_ptr<ScriptType> GetType() const {
@ -45,25 +36,48 @@ namespace Porygon::Evaluation {
return TypeClass::Function;
}
const shared_ptr<EvalValue> Clone() const final {
// We don't run make_shared here as it can't call private constructors
return shared_ptr<ScriptFunctionEvalValue>(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<BoundBlockStatement> &GetInnerBlock() const {
return _innerBlock;
}
const std::size_t GetHashCode() const final {
return _hash;
}
};
class ScriptFunctionEvalValue : public GenericFunctionEvalValue {
const std::shared_ptr<BoundBlockStatement> _innerBlock;
const std::shared_ptr<EvaluationScope> _scope;
explicit ScriptFunctionEvalValue(shared_ptr<BoundBlockStatement> innerBlock,
shared_ptr<EvaluationScope> scope,
shared_ptr<FunctionScriptType> type, size_t hash)
: GenericFunctionEvalValue(move(type), hash),
_innerBlock(std::move(innerBlock)),
_scope(std::move(scope)){
}
public:
explicit ScriptFunctionEvalValue(std::shared_ptr<BoundBlockStatement> innerBlock,
std::shared_ptr<EvaluationScope> scope,
std::shared_ptr<FunctionScriptType> type)
: GenericFunctionEvalValue(move(type), rand()),
_innerBlock(std::move(innerBlock)),
_scope(std::move(scope)){
}
const shared_ptr<EvalValue> Clone() const final {
// We don't run make_shared here as it can't call private constructors
return shared_ptr<ScriptFunctionEvalValue>(new ScriptFunctionEvalValue(_innerBlock, _scope,
dynamic_pointer_cast<FunctionScriptType>(_type), _hash));
}
const std::shared_ptr<BoundBlockStatement> &GetInnerBlock() const {
return _innerBlock;
}
const std::shared_ptr<EvaluationScope> &GetScope() const {
return _scope;

View File

@ -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<EvalValue> Evaluator::EvaluateFunctionCallExpression(const BoundExpression *expression) {
auto functionCall = (BoundFunctionCallExpression *) expression;
auto function = dynamic_pointer_cast<ScriptFunctionEvalValue>(
auto function = dynamic_pointer_cast<GenericFunctionEvalValue>(
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<FunctionScriptType>(function->GetType());
auto type = std::dynamic_pointer_cast<GenericFunctionScriptType>(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<FunctionScriptType>(type);
auto parameterKeys = scriptFunctionType->GetParameterKeys();
auto originalScope = this->_evaluationScope;
auto scriptFunction = dynamic_pointer_cast<ScriptFunctionEvalValue>(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<UserData::UserDataFunction>(function);
EvalValue* arr[parameters.size()];
for (int i = 0; i < parameters.size(); i++){
arr[i] = parameters[i].get();
}
return shared_ptr<EvalValue>(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<EvalValue> Evaluator::EvaluateFunction(const ScriptFunctionEvalValue *function,

View File

@ -1,5 +1,7 @@
#include <utility>
#include <utility>
#ifndef PORYGONLANG_SCRIPTTYPE_HPP
#define PORYGONLANG_SCRIPTTYPE_HPP
@ -101,20 +103,16 @@ namespace Porygon{
}
};
class FunctionScriptType : public ScriptType{
class GenericFunctionScriptType : public ScriptType{
shared_ptr<ScriptType> _returnType;
vector<shared_ptr<ScriptType>> _parameterTypes;
vector<shared_ptr<Binder::BoundVariableKey>> _parameterKeys;
int _scopeIndex;
public:
FunctionScriptType(std::shared_ptr<ScriptType> returnType, vector<shared_ptr<ScriptType>> parameterTypes,
vector<shared_ptr<Binder::BoundVariableKey>> parameterKeys, int scopeIndex)
: ScriptType(TypeClass::Function){
GenericFunctionScriptType(std::shared_ptr<ScriptType> returnType, vector<shared_ptr<ScriptType>> parameterTypes)
: ScriptType(TypeClass::Function){
_returnType = std::move(returnType);
_parameterTypes = std::move(parameterTypes);
_parameterKeys = std::move(parameterKeys);
_scopeIndex = scopeIndex;
}
const shared_ptr<ScriptType> GetReturnType() const{
return _returnType;
}
@ -127,6 +125,20 @@ namespace Porygon{
return _parameterTypes;
}
virtual const bool IsScriptFunction() const = 0;
};
class FunctionScriptType : public GenericFunctionScriptType{
vector<shared_ptr<Binder::BoundVariableKey>> _parameterKeys;
int _scopeIndex;
public:
FunctionScriptType(std::shared_ptr<ScriptType> returnType, vector<shared_ptr<ScriptType>> parameterTypes,
vector<shared_ptr<Binder::BoundVariableKey>> parameterKeys, int scopeIndex)
: GenericFunctionScriptType(std::move(returnType), parameterTypes){
_parameterKeys = std::move(parameterKeys);
_scopeIndex = scopeIndex;
}
const vector<shared_ptr<Binder::BoundVariableKey>> 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{

View File

@ -0,0 +1,2 @@
#include "UserDataFunction.hpp"

View File

@ -0,0 +1,36 @@
#ifndef PORYGONLANG_USERDATAFUNCTION_HPP
#define PORYGONLANG_USERDATAFUNCTION_HPP
#include <utility>
#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<GenericFunctionScriptType> 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<GenericFunctionScriptType> type) : GenericFunctionEvalValue(std::move(type), rand()){
_call = call;
_obj = obj;
}
EvalValue* Call(EvalValue* parameters[], int parameterCount){
return _call(_obj, parameters, parameterCount);
}
const shared_ptr<EvalValue> Clone() const final {
// We don't run make_shared here as it can't call private constructors
return shared_ptr<UserDataFunction>(new UserDataFunction(_call, _obj, _type, _hash));
}
};
}
#endif //PORYGONLANG_USERDATAFUNCTION_HPP

View File

@ -0,0 +1,2 @@
#include "UserDataFunctionType.hpp"

View File

@ -0,0 +1,22 @@
#ifndef PORYGONLANG_USERDATAFUNCTIONTYPE_HPP
#define PORYGONLANG_USERDATAFUNCTIONTYPE_HPP
#include <utility>
#include "../ScriptType.hpp"
namespace Porygon::UserData{
class UserDataFunctionType : public GenericFunctionScriptType{
public:
UserDataFunctionType(std::shared_ptr<ScriptType> returnType, vector<shared_ptr<ScriptType>> parameterTypes)
: GenericFunctionScriptType(std::move(returnType), std::move(parameterTypes)){
}
const bool IsScriptFunction() const final{
return false;
}
};
}
#endif //PORYGONLANG_USERDATAFUNCTIONTYPE_HPP

View File

@ -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<UserDataFunctionType>(make_shared<NumericScriptType>(true, false), vector<shared_ptr<ScriptType>>(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<GenericFunctionScriptType>(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<NumericScriptType>(true, false), {}), GetFooFunction, nullptr)
},
{
HashedString::ConstHash("Addition"),
new UserDataField(AdditionFunctionType, GetAdditionFunction, nullptr)
}
});
});
}
};
GenericFunctionScriptType* UserDataTestObject::AdditionFunctionType =
new UserDataFunctionType(make_shared<NumericScriptType>(true, false),
vector<shared_ptr<ScriptType>>{
make_shared<NumericScriptType>(true, false),
make_shared<NumericScriptType>(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