From db2d731b062640539c4adf69290e84c6a857251d Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Sat, 29 Jun 2019 19:59:42 +0200 Subject: [PATCH] Implements support for functions with the same name, but different parameters --- src/Binder/Binder.cpp | 65 ++++++---- src/Binder/Binder.hpp | 3 +- .../BoundExpressions/BoundExpression.hpp | 31 +---- .../BoundFunctionCallExpression.cpp | 1 + .../BoundFunctionCallExpression.hpp | 45 +++++++ .../BoundFunctionDeclarationStatement.hpp | 11 +- src/Diagnostics/DiagnosticCode.hpp | 5 +- src/Diagnostics/DiagnosticsHolder.cpp | 6 +- .../EvalValues/ScriptFunctionEvalValue.hpp | 78 ++++++------ src/Evaluator/Evaluator.cpp | 45 ++++--- src/Evaluator/Evaluator.hpp | 2 +- src/FunctionScriptType.cpp | 1 + src/FunctionScriptType.hpp | 112 ++++++++++++++++++ src/Script.cpp | 2 +- src/ScriptType.cpp | 5 +- src/ScriptType.hpp | 53 --------- src/StandardLibraries/BasicLibrary.hpp | 17 ++- src/StandardLibraries/StaticScope.hpp | 6 + src/UserData/UserDataFunction.cpp | 5 +- src/UserData/UserDataFunction.hpp | 20 ++-- src/UserData/UserDataFunctionType.hpp | 15 +-- src/UserData/UserDataTemplates.hpp | 10 +- tests/integration/FunctionsTests.cpp | 28 +++++ 23 files changed, 362 insertions(+), 204 deletions(-) create mode 100644 src/Binder/BoundExpressions/BoundFunctionCallExpression.cpp create mode 100644 src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp create mode 100644 src/FunctionScriptType.cpp create mode 100644 src/FunctionScriptType.hpp diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index ddfe1f5..ae66b68 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -3,8 +3,9 @@ #include "Binder.hpp" #include "../TableScriptType.hpp" #include "BoundExpressions/BoundTableExpression.hpp" +#include "BoundExpressions/BoundFunctionCallExpression.hpp" #include "../UserData/UserDataScriptType.hpp" -#include +#include "../FunctionScriptType.hpp" using namespace Porygon::Parser; @@ -158,19 +159,38 @@ namespace Porygon::Binder { auto identifier = functionStatement->GetIdentifier(); auto returnType = make_shared(TypeClass::Nil); - auto type = make_shared(returnType, parameterTypes, parameterKeys, scopeIndex); - this->_currentFunction = type; + auto option = new ScriptFunctionOption(returnType, parameterTypes, parameterKeys); + this->_currentFunction = option; - auto assignment = this->_scope->AssignVariable(identifier, type); - if (assignment.GetResult() != VariableAssignmentResult::Ok) { - this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::CantAssignVariable, statement->GetStartPosition(), - statement->GetLength()); - return new BoundBadStatement(); + shared_ptr type; + auto scope = this -> _scope -> Exists(identifier); + BoundVariableKey* assignmentKey; + if (scope >= 0){ + auto var = this -> _scope -> GetVariable(scope, identifier); + auto varType =var->GetType(); + if (varType->GetClass() != TypeClass::Function){ + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::CantAssignVariable, statement->GetStartPosition(), + statement->GetLength()); + } + type = dynamic_pointer_cast(varType); + type->RegisterFunctionOption(option); + assignmentKey = new BoundVariableKey(identifier, scope, false); + } else{ + type = make_shared(); + type->RegisterFunctionOption(option); + auto assignment = this->_scope->AssignVariable(identifier, type); + if (assignment.GetResult() != VariableAssignmentResult::Ok) { + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::CantAssignVariable, statement->GetStartPosition(), + statement->GetLength()); + return new BoundBadStatement(); + } + assignmentKey = assignment.GetKey(); } + auto boundBlock = this->BindBlockStatement(functionStatement->GetBlock()); this->_scope->GoOuterScope(); this->_currentFunction = nullptr; - return new BoundFunctionDeclarationStatement(type, assignment.GetKey(), (BoundBlockStatement *) boundBlock); + return new BoundFunctionDeclarationStatement(type, assignmentKey, (BoundBlockStatement *) boundBlock); } BoundStatement *Binder::BindReturnStatement(const ParsedStatement *statement) { @@ -558,28 +578,23 @@ namespace Porygon::Binder { return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength()); } auto functionType = std::dynamic_pointer_cast(type); - auto parameterTypes = functionType->GetParameterTypes(); auto givenParameters = expression->GetParameters(); - if (parameterTypes.size() != givenParameters->size()) { - this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::ParameterCountMismatch, + auto givenParameterTypes = vector>(givenParameters->size()); + vector boundParameters = vector(givenParameters->size()); + for (int i = 0; i < givenParameters->size(); i++){ + boundParameters[i] = this -> BindExpression(givenParameters->at(i)); + givenParameterTypes[i] = boundParameters[i]->GetType(); + } + + auto functionOption = functionType->GetFunctionOption(givenParameterTypes); + if (functionOption == nullptr){ + this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::InvalidFunctionParameters, expression->GetStartPosition(), expression->GetLength()); return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength()); } - vector boundParameters = vector(givenParameters->size()); - for (int i = 0; i < givenParameters->size(); i++) { - auto parameter = givenParameters->at(i); - auto boundParameter = this->BindExpression(parameter); - if (boundParameter->GetType().get()->operator!=(parameterTypes.at(i).get())) { - this->_scriptData->Diagnostics->LogError(Diagnostics::DiagnosticCode::ParameterTypeMismatch, - parameter->GetStartPosition(), - parameter->GetLength()); - return new BoundBadExpression(expression->GetStartPosition(), expression->GetLength()); - } - boundParameters[i] = boundParameter; - } - return new BoundFunctionCallExpression(functionExpression, boundParameters, functionType.get()->GetReturnType(), + return new BoundFunctionCallExpression(functionExpression, boundParameters, functionOption, functionOption->GetReturnType(), expression->GetStartPosition(), expression->GetLength()); } diff --git a/src/Binder/Binder.hpp b/src/Binder/Binder.hpp index b94487f..5d842c7 100644 --- a/src/Binder/Binder.hpp +++ b/src/Binder/Binder.hpp @@ -6,6 +6,7 @@ #include "../Script.hpp" #include "BoundVariables/BoundScope.hpp" #include "../Parser/ParsedExpressions/ParsedTableExpression.hpp" +#include "../FunctionScriptType.hpp" using namespace std; using namespace Porygon::Parser; @@ -14,7 +15,7 @@ namespace Porygon::Binder { class Binder { Porygon::Script *_scriptData; BoundScope *_scope; - shared_ptr _currentFunction; + GenericFunctionOption* _currentFunction; ~Binder(); diff --git a/src/Binder/BoundExpressions/BoundExpression.hpp b/src/Binder/BoundExpressions/BoundExpression.hpp index 4c99fb6..73842c5 100644 --- a/src/Binder/BoundExpressions/BoundExpression.hpp +++ b/src/Binder/BoundExpressions/BoundExpression.hpp @@ -7,6 +7,7 @@ #include "../../ScriptType.hpp" #include "../BoundOperators.hpp" #include "../BoundVariables/BoundVariableKey.hpp" +#include "../../FunctionScriptType.hpp" using namespace std; @@ -227,36 +228,6 @@ namespace Porygon::Binder { } }; - class BoundFunctionCallExpression : public BoundExpression { - const BoundExpression *_functionExpression; - const vector _parameters; - public: - BoundFunctionCallExpression(BoundExpression *functionExpression, vector parameters, - shared_ptr result, - unsigned int start, unsigned int length) - : BoundExpression(start, length, std::move(result)), _functionExpression(functionExpression), - _parameters(std::move(parameters)) {} - - ~BoundFunctionCallExpression() final { - delete _functionExpression; - for (auto p : _parameters) { - delete p; - } - } - - const BoundExpressionKind GetKind() const final { - return BoundExpressionKind::FunctionCall; - } - - const BoundExpression *GetFunctionExpression() const { - return _functionExpression; - } - - const vector *GetParameters() const { - return &_parameters; - } - }; - class BoundIndexExpression : public BoundExpression { const BoundExpression *_indexableExpression; const BoundExpression *_indexExpression; diff --git a/src/Binder/BoundExpressions/BoundFunctionCallExpression.cpp b/src/Binder/BoundExpressions/BoundFunctionCallExpression.cpp new file mode 100644 index 0000000..fb4825d --- /dev/null +++ b/src/Binder/BoundExpressions/BoundFunctionCallExpression.cpp @@ -0,0 +1 @@ +#include "BoundFunctionCallExpression.hpp" diff --git a/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp b/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp new file mode 100644 index 0000000..184a216 --- /dev/null +++ b/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp @@ -0,0 +1,45 @@ +#ifndef PORYGONLANG_BOUNDFUNCTIONCALLEXPRESSION_HPP +#define PORYGONLANG_BOUNDFUNCTIONCALLEXPRESSION_HPP + +#include "BoundExpression.hpp" + +namespace Porygon::Binder { + class BoundFunctionCallExpression : public Porygon::Binder::BoundExpression { + const BoundExpression *_functionExpression; + const vector _parameters; + const Porygon::GenericFunctionOption *_option; + public: + BoundFunctionCallExpression(BoundExpression *functionExpression, vector parameters, + Porygon::GenericFunctionOption *option, shared_ptr result, + unsigned int start, unsigned int length) + : BoundExpression(start, length, move(result)), _functionExpression(functionExpression), + _parameters(move(parameters)), _option(option) {} + + ~BoundFunctionCallExpression() final { + delete _functionExpression; + for (auto p : _parameters) { + delete p; + } + } + + const Porygon::Binder::BoundExpressionKind GetKind() const final { + return Porygon::Binder::BoundExpressionKind::FunctionCall; + } + + const BoundExpression *GetFunctionExpression() const { + return _functionExpression; + } + + const vector *GetParameters() const { + return &_parameters; + } + + const Porygon::GenericFunctionOption *GetFunctionOption() const { + return _option; + } + }; +} + +#include "BoundExpression.hpp" + +#endif //PORYGONLANG_BOUNDFUNCTIONCALLEXPRESSION_HPP diff --git a/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp b/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp index 6c2b3a1..05936c2 100644 --- a/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp +++ b/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp @@ -1,19 +1,18 @@ -#include - #ifndef PORYGONLANG_BOUNDFUNCTIONDECLARATIONSTATEMENT_HPP #define PORYGONLANG_BOUNDFUNCTIONDECLARATIONSTATEMENT_HPP #include #include "BoundStatement.hpp" +#include "../../FunctionScriptType.hpp" namespace Porygon::Binder { class BoundFunctionDeclarationStatement : public BoundStatement { const BoundVariableKey *_key; const std::shared_ptr _block; - const std::shared_ptr _type; + const std::shared_ptr _type; public: - BoundFunctionDeclarationStatement(std::shared_ptr type, BoundVariableKey *key, + BoundFunctionDeclarationStatement(std::shared_ptr type, BoundVariableKey *key, BoundBlockStatement *block) : _key(key), _block(block), _type(std::move(type)) { } @@ -34,12 +33,10 @@ namespace Porygon::Binder { return _block; } - const std::shared_ptr GetType() const { + const std::shared_ptr GetType() const { return _type; } }; } -#include "BoundStatement.hpp" - #endif //PORYGONLANG_BOUNDFUNCTIONDECLARATIONSTATEMENT_HPP diff --git a/src/Diagnostics/DiagnosticCode.hpp b/src/Diagnostics/DiagnosticCode.hpp index 53534d6..72e4bb9 100644 --- a/src/Diagnostics/DiagnosticCode.hpp +++ b/src/Diagnostics/DiagnosticCode.hpp @@ -17,8 +17,6 @@ namespace Porygon::Diagnostics { CantAssignVariable, VariableNotFound, ExpressionIsNotAFunction, - ParameterCountMismatch, - ParameterTypeMismatch, CantIndex, InvalidReturnType, ConditionNotABool, @@ -27,7 +25,8 @@ namespace Porygon::Diagnostics { UserDataFieldNoGetter, UserDataFieldNoSetter, NumericalForArgumentNotANumber, - CantIterateExpression + CantIterateExpression, + InvalidFunctionParameters }; } #endif //PORYGONLANG_DIAGNOSTICCODE_HPP diff --git a/src/Diagnostics/DiagnosticsHolder.cpp b/src/Diagnostics/DiagnosticsHolder.cpp index 991711f..9fa7e5f 100644 --- a/src/Diagnostics/DiagnosticsHolder.cpp +++ b/src/Diagnostics/DiagnosticsHolder.cpp @@ -48,16 +48,16 @@ size_t DiagnosticsHolder::GetLineFromPosition(size_t i) { if (bottomLimit == topLimit){ return bottomLimit; } - size_t half = (topLimit - bottomLimit) / 2; + size_t half = bottomLimit + ((topLimit - bottomLimit) / 2); size_t pos = _lineStarts[half]; size_t length = _lineLength[half]; if (pos < i && pos + length > i){ return half; } if (pos > i){ - bottomLimit = half; - } else if (pos < i){ topLimit = half; + } else if (pos < i){ + bottomLimit = half; } else{ return half; } diff --git a/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp b/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp index 2f1dbc9..a080201 100644 --- a/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp +++ b/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp @@ -4,24 +4,63 @@ #include #include +#include #include "../../ScriptType.hpp" #include "EvalValue.hpp" #include "../../Binder/BoundStatements/BoundStatement.hpp" #include "../EvaluationScope/EvaluationScope.hpp" +#include "../../FunctionScriptType.hpp" using namespace std; namespace Porygon::Evaluation { + class GenericFunctionOption{ + public: + virtual ~GenericFunctionOption() = default; + }; + + class EvaluationScriptFunctionOption : public GenericFunctionOption{ + const std::shared_ptr _innerBlock; + const std::shared_ptr _scope; + + public: + EvaluationScriptFunctionOption(const shared_ptr innerBlock, const shared_ptr scope) + : _innerBlock(innerBlock), _scope(scope) { + + } + + const std::shared_ptr &GetInnerBlock() const { + return _innerBlock; + } + + const std::shared_ptr &GetScope() const { + return _scope; + } + + + }; + class GenericFunctionEvalValue : public EvalValue{ protected: const std::shared_ptr _type; const std::size_t _hash; - + std::vector> _options; public: GenericFunctionEvalValue(shared_ptr type, size_t hash) : _type(move(type)), _hash(hash){ + } + const shared_ptr Clone() const final { + auto t = make_shared(_type, _hash); + for (const auto& o: _options){ + t->_options.push_back(o); + } + return t; + } + + void RegisterOption(GenericFunctionOption* option){ + _options.push_back(shared_ptr(option)); } const std::shared_ptr GetType() const { @@ -41,42 +80,9 @@ namespace Porygon::Evaluation { 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; + const shared_ptr GetOption(const size_t id) const{ + return this->_options.at(id); } }; } diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index bdb68b1..00b8fd9 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -8,10 +8,12 @@ #include "EvalValues/ScriptFunctionEvalValue.hpp" #include "EvalValues/TableEvalValue.hpp" #include "../Binder/BoundExpressions/BoundTableExpression.hpp" +#include "../Binder/BoundExpressions/BoundFunctionCallExpression.hpp" #include "../TableScriptType.hpp" #include "../UserData/UserDataFunction.hpp" #include "../Utilities/StringUtils.hpp" #include "EvalValues/NumericalTableEvalValue.hpp" +#include "../FunctionScriptType.hpp" using namespace std; using namespace Porygon::Binder; @@ -96,11 +98,16 @@ namespace Porygon::Evaluation { auto type = statement->GetType(); auto key = statement->GetKey(); auto block = statement->GetBlock(); - auto value = make_shared(block, this->_evaluationScope, type); + auto option = new Evaluation::EvaluationScriptFunctionOption(block, this->_evaluationScope); + if (key->IsCreation()) { + auto value = make_shared(type, rand()); + value->RegisterOption(option); this->_evaluationScope->CreateVariable(key, value); } else { - this->_evaluationScope->SetVariable(key, value); + auto var = dynamic_pointer_cast(this -> _evaluationScope ->GetVariable(key)); + var->RegisterOption(option); + this->_evaluationScope->SetVariable(key, var); } } @@ -370,20 +377,23 @@ namespace Porygon::Evaluation { } auto type = std::dynamic_pointer_cast(function->GetType()); - if (type -> IsScriptFunction()){ - auto parameterTypes = type->GetParameterTypes(); - auto scriptFunctionType = std::dynamic_pointer_cast(type); + auto func = dynamic_pointer_cast(function); + auto option = functionCall ->GetFunctionOption(); + auto opt = func->GetOption(option->GetOptionId()); + if (option -> IsScriptFunction()){ + auto parameterTypes = option->GetParameterTypes(); + auto scriptFunctionType = (ScriptFunctionOption*)option; auto parameterKeys = scriptFunctionType->GetParameterKeys(); auto originalScope = this->_evaluationScope; - auto scriptFunction = dynamic_pointer_cast(function); - this->_evaluationScope = scriptFunction->GetScope(); + auto scriptOption = dynamic_pointer_cast(opt); + this->_evaluationScope = scriptOption->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()); } - this->EvaluateBlockStatement(scriptFunction->GetInnerBlock().get()); + this->EvaluateBlockStatement(scriptOption->GetInnerBlock().get()); this->_evaluationScope = originalScope; this->_hasReturned = false; @@ -391,30 +401,33 @@ namespace Porygon::Evaluation { this->_returnValue = nullptr; return r; } else{ - auto userDataFunction = dynamic_pointer_cast(function); + auto scriptOption = dynamic_pointer_cast(opt); 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())); + return shared_ptr(scriptOption -> Call(arr, parameters.size())); } } - const shared_ptr Evaluator::EvaluateFunction(const ScriptFunctionEvalValue *function, + const shared_ptr Evaluator::EvaluateFunction(const GenericFunctionEvalValue *function, const vector ¶meters) { - auto type = std::dynamic_pointer_cast(function->GetType()); - auto parameterTypes = type->GetParameterTypes(); - auto parameterKeys = type->GetParameterKeys(); + auto type = std::dynamic_pointer_cast(function->GetType()); + auto option = (ScriptFunctionOption*)type-> GetFirstOption(); + + auto parameterTypes = option->GetParameterTypes(); + auto parameterKeys = option->GetParameterKeys(); auto originalScope = this->_evaluationScope; - this->_evaluationScope = function->GetScope(); + auto scriptOption = dynamic_pointer_cast(function->GetOption(0)); + this->_evaluationScope = scriptOption->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()); } - this->EvaluateBlockStatement(function->GetInnerBlock().get()); + this->EvaluateBlockStatement(scriptOption->GetInnerBlock().get()); this->_evaluationScope = originalScope; this->_hasReturned = false; auto r = this->_returnValue; diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index 8dab63a..349432c 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -68,7 +68,7 @@ namespace Porygon::Evaluation{ } EvalValue* Evaluate(const BoundScriptStatement* statement); - const shared_ptr EvaluateFunction(const ScriptFunctionEvalValue *function, + const shared_ptr EvaluateFunction(const GenericFunctionEvalValue *function, const vector ¶meters); EvalValue* GetLastValue(){ diff --git a/src/FunctionScriptType.cpp b/src/FunctionScriptType.cpp new file mode 100644 index 0000000..3b6046c --- /dev/null +++ b/src/FunctionScriptType.cpp @@ -0,0 +1 @@ +#include "FunctionScriptType.hpp" diff --git a/src/FunctionScriptType.hpp b/src/FunctionScriptType.hpp new file mode 100644 index 0000000..5eebfd1 --- /dev/null +++ b/src/FunctionScriptType.hpp @@ -0,0 +1,112 @@ + +#ifndef PORYGONLANG_FUNCTIONSCRIPTTYPE_HPP +#define PORYGONLANG_FUNCTIONSCRIPTTYPE_HPP + +#include "ScriptType.hpp" + +namespace Porygon { + class GenericFunctionOption{ + shared_ptr _returnType; + vector> _parameterTypes; + size_t _option = 0; + public: + GenericFunctionOption(shared_ptr returnType, vector> parameterTypes){ + _returnType = move(returnType); + _parameterTypes = move(parameterTypes); + } + + virtual ~GenericFunctionOption() = default; + + const shared_ptr GetReturnType() const { + return _returnType; + } + + void SetOption(size_t v){ + _option = v; + } + + void SetReturnType(shared_ptr t) { + _returnType = move(t); + } + + const vector> GetParameterTypes() const { + return _parameterTypes; + } + + const bool IsValid(const vector>& parameters){ + if (parameters.size() != _parameterTypes.size()){ + return false; + } + for (int i = 0; i < parameters.size(); i++){ + if (parameters[i]->operator!=(_parameterTypes[i].get())){ + return false; + } + } + return true; + } + + const size_t GetOptionId() const{ + return _option; + } + + virtual const bool IsScriptFunction() const = 0; + }; + + class GenericFunctionScriptType : public Porygon::ScriptType { + vector _options; + public: + GenericFunctionScriptType() + : ScriptType(Porygon::TypeClass::Function){}; + + explicit GenericFunctionScriptType(GenericFunctionOption * option) + : ScriptType(Porygon::TypeClass::Function){ + this -> RegisterFunctionOption(option); + }; + + virtual ~GenericFunctionScriptType() final{ + for (auto o: _options){ + delete o; + } + } + + void RegisterFunctionOption (GenericFunctionOption * opt){ + opt->SetOption(_options.size()); + _options.push_back(opt); + } + + GenericFunctionOption* GetFunctionOption(const vector>& parameters){ + for (auto o: _options){ + if (o->IsValid(parameters)){ + return o; + } + } + return nullptr; + } + + GenericFunctionOption* GetFirstOption(){ + return _options[0]; + } + }; + + class ScriptFunctionOption : public GenericFunctionOption { + vector> _parameterKeys; + public: + ScriptFunctionOption(shared_ptr returnType, vector> parameterTypes, + vector> parameterKeys) + : GenericFunctionOption(move(returnType), std::move(parameterTypes)) { + _parameterKeys = move(parameterKeys); + } + + const vector> GetParameterKeys() const { + return _parameterKeys; + } + + const bool IsScriptFunction() const final { + return true; + } + }; +} + +#include "ScriptType.hpp" + +#endif //PORYGONLANG_FUNCTIONSCRIPTTYPE_HPP diff --git a/src/Script.cpp b/src/Script.cpp index 4b2c241..5679cae 100644 --- a/src/Script.cpp +++ b/src/Script.cpp @@ -85,7 +85,7 @@ bool Porygon::Script::HasFunction(const u16string &key) { } shared_ptr Porygon::Script::CallFunction(const u16string &key, const vector& variables) { - auto var = (ScriptFunctionEvalValue*)GetVariable(key); + auto var = (GenericFunctionEvalValue*)GetVariable(key); return this->_evaluator->EvaluateFunction(var, variables); } diff --git a/src/ScriptType.cpp b/src/ScriptType.cpp index e15fbe9..8ce6790 100644 --- a/src/ScriptType.cpp +++ b/src/ScriptType.cpp @@ -32,7 +32,10 @@ namespace Porygon{ for (int i = 0; i < parameterCount; i++){ vector[i] = shared_ptr(parameters[i]); } - return new UserData::UserDataFunctionType(shared_ptr(returnType), vector); + auto option = new UserData::UserDataFunctionOption(shared_ptr(returnType), vector); + auto type = new GenericFunctionScriptType(); + type->RegisterFunctionOption(option); + return type; } } } diff --git a/src/ScriptType.hpp b/src/ScriptType.hpp index 29024e3..acfbb29 100644 --- a/src/ScriptType.hpp +++ b/src/ScriptType.hpp @@ -1,7 +1,3 @@ -#include - -#include - #ifndef PORYGONLANG_SCRIPTTYPE_HPP #define PORYGONLANG_SCRIPTTYPE_HPP @@ -110,55 +106,6 @@ namespace Porygon{ } }; - class GenericFunctionScriptType : public ScriptType{ - shared_ptr _returnType; - vector> _parameterTypes; - public: - GenericFunctionScriptType(std::shared_ptr returnType, vector> parameterTypes) - : ScriptType(TypeClass::Function){ - _returnType = std::move(returnType); - _parameterTypes = std::move(parameterTypes); - } - - const shared_ptr GetReturnType() const{ - return _returnType; - } - - void SetReturnType(shared_ptr t){ - _returnType = std::move(t); - } - - const vector> GetParameterTypes() const{ - 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; - } - - const int GetScopeIndex() const{ - return _scopeIndex; - } - - const bool IsScriptFunction() const final{ - return true; - } - }; - class NumericalTableScriptType : public ScriptType{ shared_ptr _valueType; // Consider adding a check whether the table actually contains a type if every key is static. diff --git a/src/StandardLibraries/BasicLibrary.hpp b/src/StandardLibraries/BasicLibrary.hpp index 5a6bb7b..2044487 100644 --- a/src/StandardLibraries/BasicLibrary.hpp +++ b/src/StandardLibraries/BasicLibrary.hpp @@ -32,19 +32,28 @@ namespace Porygon::StandardLibraries{ static void RegisterVariables(std::map* bound, std::map>* values){ // Register error function - auto errorFuncType = make_shared( + auto errorFuncTypeOption = new UserData::UserDataFunctionOption( make_shared(TypeClass::Nil), vector>{make_shared(false, 0)}); - auto errorFunc = make_shared(_error, nullptr); + auto errorFuncType = make_shared(); + errorFuncType->RegisterFunctionOption(errorFuncTypeOption); + auto errorFuncOption = new UserData::UserDataFunction(_error, nullptr); + auto errorFunc = make_shared(errorFuncType, rand()); + errorFunc->RegisterOption(errorFuncOption); auto errorLookup = Utilities::HashedString::CreateLookup(u"error"); bound->insert({errorLookup, new Binder::BoundVariable(errorFuncType)}); values->insert({errorLookup, errorFunc}); // Register assert function - auto assertFuncType = make_shared( + auto assertFuncTypeOption = new UserData::UserDataFunctionOption( make_shared(TypeClass::Bool), vector>{make_shared(TypeClass::Bool)}); - auto assertFunc = make_shared(_assert, nullptr); + auto assertFuncType = make_shared(); + assertFuncType->RegisterFunctionOption(assertFuncTypeOption); + auto assertFuncOption = new UserData::UserDataFunction(_assert, nullptr); + auto assertFunc = make_shared(assertFuncType, rand()); + assertFunc->RegisterOption(assertFuncOption); + auto assertLookup = Utilities::HashedString::CreateLookup(u"assert"); bound->insert({assertLookup, new Binder::BoundVariable(assertFuncType)}); values->insert({assertLookup, assertFunc}); diff --git a/src/StandardLibraries/StaticScope.hpp b/src/StandardLibraries/StaticScope.hpp index de2cf87..7c552f8 100644 --- a/src/StandardLibraries/StaticScope.hpp +++ b/src/StandardLibraries/StaticScope.hpp @@ -24,6 +24,12 @@ namespace Porygon::StandardLibraries{ InternalScope(){ BasicLibrary::RegisterVariables(&_boundVariables, &_variables); } + + ~InternalScope(){ + for (const auto& b: _boundVariables){ + delete b.second; + } + } }; static InternalScope _internal; diff --git a/src/UserData/UserDataFunction.cpp b/src/UserData/UserDataFunction.cpp index 0d3e990..a729c9e 100644 --- a/src/UserData/UserDataFunction.cpp +++ b/src/UserData/UserDataFunction.cpp @@ -6,7 +6,10 @@ using namespace Porygon::Evaluation; namespace Porygon::UserData{ extern "C" { EvalValue * CreateFunctionEvalValue(Evaluation::EvalValue* (*func)(void* , EvalValue* [], int ), void* obj) { - return new UserDataFunction(func, obj); + auto opt = new UserDataFunction(func, obj); + auto t = new GenericFunctionEvalValue(make_shared(), rand()); + t->RegisterOption(opt); + return t; } } } diff --git a/src/UserData/UserDataFunction.hpp b/src/UserData/UserDataFunction.hpp index 0ba8f64..5635378 100644 --- a/src/UserData/UserDataFunction.hpp +++ b/src/UserData/UserDataFunction.hpp @@ -4,32 +4,28 @@ #include #include "../Evaluator/EvalValues/ScriptFunctionEvalValue.hpp" #include "UserDataFunctionType.hpp" +#include "../FunctionScriptType.hpp" namespace Porygon::UserData{ - class UserDataFunction : public Evaluation::GenericFunctionEvalValue { - Evaluation::EvalValue* (*_call)(void* obj, EvalValue* parameters[], int parameterCount); + class UserDataFunction : public Evaluation::GenericFunctionOption { + Evaluation::EvalValue* (*_call)(void* obj, Evaluation::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){ + UserDataFunction(Evaluation::EvalValue* (*call)(void* obj, Evaluation::EvalValue* parameters[], int parameterCount), void* obj, + shared_ptr type, size_t hash) : GenericFunctionOption(){ _call = call; _obj = obj; } public: - UserDataFunction(Evaluation::EvalValue* (*call)(void* obj, EvalValue* parameters[], int parameterCount), void* obj) : - GenericFunctionEvalValue(make_shared(nullptr, vector>{}), rand()){ + UserDataFunction(Evaluation::EvalValue* (*call)(void* obj, Evaluation::EvalValue* parameters[], int parameterCount), void* obj) : + GenericFunctionOption(){ _call = call; _obj = obj; } - EvalValue* Call(EvalValue* parameters[], int parameterCount){ + Evaluation::EvalValue* Call(Evaluation::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)); - } }; } diff --git a/src/UserData/UserDataFunctionType.hpp b/src/UserData/UserDataFunctionType.hpp index f7dc352..1530f7d 100644 --- a/src/UserData/UserDataFunctionType.hpp +++ b/src/UserData/UserDataFunctionType.hpp @@ -3,26 +3,27 @@ #include #include "../ScriptType.hpp" +#include "../FunctionScriptType.hpp" namespace Porygon::UserData{ - class UserDataFunctionType : public GenericFunctionScriptType{ + class UserDataFunctionOption : public GenericFunctionOption{ public: - UserDataFunctionType(std::shared_ptr returnType, vector> parameterTypes) - : GenericFunctionScriptType(std::move(returnType), std::move(parameterTypes)){ + UserDataFunctionOption(std::shared_ptr returnType, vector> parameterTypes) + : GenericFunctionOption(std::move(returnType), std::move(parameterTypes)) { } - static UserDataFunctionType* FromRawPointers(ScriptType* returnType, vector parameterTypes){ + static UserDataFunctionOption* FromRawPointers(ScriptType* returnType, vector parameterTypes){ auto rt = shared_ptr(returnType); auto p = vector>(parameterTypes.size()); for (int i = 0; i < parameterTypes.size(); i++){ p[i] = shared_ptr(parameterTypes[i]); } - return new UserDataFunctionType(rt, p); + return new UserDataFunctionOption(rt, p); } - static UserDataFunctionType* FromRawPointers(ScriptType* returnType){ + static UserDataFunctionOption* FromRawPointers(ScriptType* returnType){ auto rt = shared_ptr(returnType); - return new UserDataFunctionType(rt, {}); + return new UserDataFunctionOption(rt, {}); } diff --git a/src/UserData/UserDataTemplates.hpp b/src/UserData/UserDataTemplates.hpp index 9cb09d5..bdfe3b9 100644 --- a/src/UserData/UserDataTemplates.hpp +++ b/src/UserData/UserDataTemplates.hpp @@ -67,14 +67,18 @@ #define PORYGON_FUNCTION(fieldName, returnType, ...) \ { \ Porygon::Utilities::HashedString::ConstHash(#fieldName), \ - new Porygon::UserData::UserDataField(Porygon::UserData::UserDataFunctionType::FromRawPointers(returnType, {__VA_ARGS__} ), \ + new Porygon::UserData::UserDataField( \ + new Porygon::GenericFunctionScriptType( \ + Porygon::UserData::UserDataFunctionOption::FromRawPointers(returnType, {__VA_ARGS__} )), \ \ \ [](void* obj) -> Porygon::Evaluation::EvalValue* { \ - return new Porygon::UserData::UserDataFunction( \ + auto t = new Porygon::Evaluation::GenericFunctionEvalValue(make_shared(), rand()); \ + t->RegisterOption(new Porygon::UserData::UserDataFunction( \ [](void* obj, Porygon::Evaluation::EvalValue* par[], int parameterCount) \ -> Porygon::Evaluation::EvalValue*{return ((T_USERDATA*)obj)->invoke__##fieldName(obj, par, parameterCount);}, \ - obj);}, \ + obj)); \ + return t;}, \ nullptr) \ }, diff --git a/tests/integration/FunctionsTests.cpp b/tests/integration/FunctionsTests.cpp index 3261c79..60afdf2 100644 --- a/tests/integration/FunctionsTests.cpp +++ b/tests/integration/FunctionsTests.cpp @@ -150,5 +150,33 @@ return delete script; } +TEST_CASE( "Allow declaration of multiple functions with different signatures", "[integration]" ) { + Script* script = Script::Create( + R"( +function add(number a, number b) + return a + b +end + +function add(string a, string b) + return a + b +end + +intResult = add(5, 500) +stringResult = add('foo', 'bar') +)" + ); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto intVar = script -> GetVariable(u"intResult"); + REQUIRE(intVar != nullptr); + CHECK(intVar->EvaluateInteger() == 505); + auto stringVar = script -> GetVariable(u"stringResult"); + REQUIRE(stringVar != nullptr); + CHECK(stringVar->EvaluateString() == u"foobar"); + + + delete script; +} + #endif