diff --git a/src/Binder/Binder.cpp b/src/Binder/Binder.cpp index ddb7d3c..cf80a6a 100644 --- a/src/Binder/Binder.cpp +++ b/src/Binder/Binder.cpp @@ -24,6 +24,7 @@ BoundStatement* Binder::BindStatement(ParsedStatement* statement){ case ParsedStatementKind ::Block: return this -> BindBlockStatement(statement); case ParsedStatementKind ::Expression: return this -> BindExpressionStatement(statement); case ParsedStatementKind::Assignment: return this -> BindAssignmentStatement(statement); + case ParsedStatementKind ::FunctionDeclaration: return this->BindFunctionDeclarationStatement(statement); case ParsedStatementKind::Bad: return new BoundBadStatement(); } @@ -62,6 +63,41 @@ BoundStatement* Binder::BindAssignmentStatement(ParsedStatement *statement){ } } +ScriptType* ParseTypeIdentifier(HashedString s){ + switch (s.GetHash()){ + case HashedString::ConstHash("number"): return new NumericScriptType(false, false); + case HashedString::ConstHash("bool"): return new ScriptType(TypeClass::Bool); + case HashedString::ConstHash("string"): return new ScriptType(TypeClass::String); + default: return new ScriptType(TypeClass::Error); // todo: change to userdata + } +} + +BoundStatement *Binder::BindFunctionDeclarationStatement(ParsedStatement *statement) { + auto functionStatement = (ParsedFunctionDeclarationStatement*) statement; + auto parameters = functionStatement->GetParameters(); + vector parameterTypes = vector(parameters.size()); + vector parameterKeys = vector(parameters.size()); + this->_scope->GoInnerScope(); + auto scopeId = this->_scope->GetCurrentScope(); + for (int i = 0; i < parameters.size(); i++){ + auto var = parameters[i]; + auto parsedType = ParseTypeIdentifier(var->GetType()); + parameterTypes[i] = parsedType; + parameterKeys[i] = var->GetIdentifier().GetHash(); + this->_scope->CreateExplicitLocal(var->GetIdentifier().GetHash(), *parsedType); + } + auto boundBlock = this -> BindBlockStatement(functionStatement->GetBlock()); + this->_scope->GoOuterScope(); + auto identifier = functionStatement->GetIdentifier(); + auto returnType = new ScriptType(TypeClass::Nil); + auto type = new FunctionScriptType(returnType, parameterTypes, parameterKeys, scopeId); + auto assignment = this->_scope->AssignVariable(identifier.GetHash(), *type); + if (assignment.GetResult() == VariableAssignmentResult::Ok){ + return new BoundFunctionDeclarationStatement(type, assignment.GetKey(), (BoundBlockStatement*)boundBlock); + } + return new BoundBadStatement(); +} + BoundExpression* Binder::BindExpression(ParsedExpression* expression){ switch (expression -> GetKind()){ case ParsedExpressionKind ::LiteralInteger: @@ -226,3 +262,4 @@ BoundExpression* Binder::BindUnaryOperator(UnaryExpression* expression){ } + diff --git a/src/Binder/Binder.hpp b/src/Binder/Binder.hpp index 3bcaf11..520ed5e 100644 --- a/src/Binder/Binder.hpp +++ b/src/Binder/Binder.hpp @@ -17,6 +17,7 @@ class Binder { BoundStatement *BindBlockStatement(ParsedStatement *statement); BoundStatement *BindExpressionStatement(ParsedStatement *statement); BoundStatement *BindAssignmentStatement(ParsedStatement *statement); + BoundStatement *BindFunctionDeclarationStatement(ParsedStatement * statement); BoundExpression *BindExpression(ParsedExpression *expression); BoundExpression *BindVariableExpression(VariableExpression *expression); diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index ec5646a..920f192 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -16,6 +16,7 @@ enum class BoundStatementKind{ Block, Expression, Assignment, + FunctionDeclaration, }; class BoundStatement{ @@ -114,4 +115,32 @@ public: } }; +class BoundFunctionDeclarationStatement : public BoundStatement{ + BoundVariableKey* _key; + BoundBlockStatement* _block; + FunctionScriptType* _type; +public: + BoundFunctionDeclarationStatement(FunctionScriptType* type, BoundVariableKey* key, BoundBlockStatement* block){ + _key = key; + _block = block; + _type = type; + } + + BoundStatementKind GetKind() final{ + return BoundStatementKind ::FunctionDeclaration; + } + + BoundVariableKey* GetKey(){ + return _key; + } + + BoundBlockStatement* GetBlock(){ + return _block; + } + + FunctionScriptType* GetType(){ + return _type; + } +}; + #endif //PORYGONLANG_BOUNDSTATEMENT_HPP diff --git a/src/Binder/BoundVariables/BoundScope.hpp b/src/Binder/BoundVariables/BoundScope.hpp index 3e0ca0d..a0db2b2 100644 --- a/src/Binder/BoundVariables/BoundScope.hpp +++ b/src/Binder/BoundVariables/BoundScope.hpp @@ -32,6 +32,10 @@ public: int GetDeepestScope(){ return _deepestScope; } + + int GetCurrentScope(){ + return _currentScope; + } }; diff --git a/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp b/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp new file mode 100644 index 0000000..18e98e5 --- /dev/null +++ b/src/Evaluator/EvalValues/ScriptFunctionEvalValue.hpp @@ -0,0 +1,57 @@ + +#ifndef PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP +#define PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP + +#include "../../ScriptType.hpp" +#include "EvalValue.hpp" +#include "../../Binder/BoundStatements/BoundStatement.hpp" +#include "../Evaluator.hpp" + +class ScriptFunctionEvalValue : public EvalValue{ + BoundBlockStatement* _innerBlock; + FunctionScriptType _type; +public: + explicit ScriptFunctionEvalValue(BoundBlockStatement* innerBlock, const FunctionScriptType& type) + : _type(type) + { + _innerBlock = innerBlock; + } + + EvalValue* Clone() final{ + return new ScriptFunctionEvalValue(_innerBlock, _type); + } + + ~ScriptFunctionEvalValue() final{ + delete _innerBlock; + } + + ScriptType* GetType() final{ + return &_type; + }; + + bool operator ==(EvalValue* b) final{ + if (b->GetType()->GetClass() != TypeClass::Function) + return false; + return this->_innerBlock == ((ScriptFunctionEvalValue*)b)->_innerBlock; + }; + + EvalValue* EvaluateFunction(Evaluator* evaluator, const vector& parameters){ + auto parameterTypes = _type.GetParameterTypes(); + auto parameterKeys = _type.GetParameterKeys(); + auto scope = evaluator->GetScope(); + for (int i = 0; i < parameterTypes.size() && i < parameterKeys.size() && i < parameters.size(); i++){ + auto parameter = parameters[i]; + auto requiredType = parameterTypes[i]; + if (parameter->GetType() != requiredType){ + throw EvaluationException("Passed wrong type to function."); + } + auto key = parameterKeys[i]; + scope->CreateVariable(_type.GetScopeId(), key, parameter->Clone()); + } + evaluator->EvaluateBlockStatement(_innerBlock); + return nullptr; + } +}; + + +#endif //PORYGONLANG_SCRIPTFUNCTIONEVALVALUE_HPP diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index ff207ef..1adc7b9 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -3,6 +3,7 @@ #include "EvaluationException.hpp" #include "../Script.hpp" #include "EvaluationScope/EvaluationScope.hpp" +#include "EvalValues/ScriptFunctionEvalValue.hpp" void Evaluator::Evaluate(BoundScriptStatement *statement) { this->_evaluationScope = new EvaluationScope(this->_scriptData->_scriptVariables, statement->GetDeepestScope()); @@ -15,6 +16,7 @@ void Evaluator::EvaluateStatement(BoundStatement *statement) { case BoundStatementKind ::Block: return this -> EvaluateBlockStatement((BoundBlockStatement*)statement); case BoundStatementKind ::Expression: return this -> EvaluateExpressionStatement((BoundExpressionStatement*)statement); case BoundStatementKind ::Assignment: return this -> EvaluateAssignmentStatement((BoundAssignmentStatement*)statement); + case BoundStatementKind ::FunctionDeclaration: return this->EvaluateFunctionDeclarationStatement((BoundFunctionDeclarationStatement*)statement); case BoundStatementKind::Bad: throw; @@ -46,6 +48,18 @@ void Evaluator::EvaluateAssignmentStatement(BoundAssignmentStatement *statement) } } +void Evaluator::EvaluateFunctionDeclarationStatement(BoundFunctionDeclarationStatement *statement) { + auto type = statement->GetType(); + auto key = statement->GetKey(); + auto block = statement->GetBlock(); + auto value = new ScriptFunctionEvalValue(block, *type); + if (key->IsCreation()){ + this->_evaluationScope->CreateVariable(key->GetScopeId(), key->GetIdentifier(), value); + } else{ + this->_evaluationScope->SetVariable(key->GetScopeId(), key->GetIdentifier(), value); + } +} + EvalValue *Evaluator::EvaluateExpression(BoundExpression *expression) { auto type = expression -> GetType(); switch (type->GetClass()){ diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp index a43b59f..f6575aa 100644 --- a/src/Evaluator/Evaluator.hpp +++ b/src/Evaluator/Evaluator.hpp @@ -20,9 +20,9 @@ class Evaluator { EvaluationScope* _evaluationScope; void EvaluateStatement(BoundStatement* statement); - void EvaluateBlockStatement(BoundBlockStatement* statement); void EvaluateExpressionStatement(BoundExpressionStatement* statement); void EvaluateAssignmentStatement(BoundAssignmentStatement* statement); + void EvaluateFunctionDeclarationStatement(BoundFunctionDeclarationStatement *statement); EvalValue* EvaluateExpression(BoundExpression* expression); NumericEvalValue* EvaluateIntegerExpression(BoundExpression* expression); @@ -50,7 +50,11 @@ public: } void Evaluate(BoundScriptStatement* statement); + void EvaluateBlockStatement(BoundBlockStatement* statement); + EvaluationScope* GetScope(){ + return _evaluationScope; + } }; diff --git a/src/Evaluator/UnaryEvaluation.cpp b/src/Evaluator/UnaryEvaluation.cpp index 10312bf..848f976 100644 --- a/src/Evaluator/UnaryEvaluation.cpp +++ b/src/Evaluator/UnaryEvaluation.cpp @@ -37,3 +37,4 @@ BooleanEvalValue *Evaluator::EvaluateBooleanUnary(BoundUnaryExpression *expressi throw; } } + diff --git a/src/ScriptType.hpp b/src/ScriptType.hpp index 5240e84..cf498f6 100644 --- a/src/ScriptType.hpp +++ b/src/ScriptType.hpp @@ -1,7 +1,9 @@ - #ifndef PORYGONLANG_SCRIPTTYPE_HPP #define PORYGONLANG_SCRIPTTYPE_HPP +#include +#include + enum class TypeClass{ Error, Nil, @@ -55,4 +57,41 @@ public: } }; +class FunctionScriptType : public ScriptType{ + ScriptType* _returnType; + std::vector _parameterTypes; + std::vector _parameterKeys; + int _scopeId; +public: + FunctionScriptType(ScriptType* returnType, std::vector parameterTypes, std::vector parameterKeys, int scopeId) + : ScriptType(TypeClass::Function){ + _returnType = returnType; + _parameterTypes = std::move(parameterTypes); + _parameterKeys = std::move(parameterKeys); + _scopeId = scopeId; + } + ~FunctionScriptType() final{ + delete _returnType; + for (auto t: _parameterTypes){ + delete t; + } + } + + ScriptType* GetReturnType(){ + return _returnType; + } + + std::vector GetParameterTypes(){ + return _parameterTypes; + } + + std::vector GetParameterKeys(){ + return _parameterKeys; + } + + int GetScopeId(){ + return _scopeId; + } +}; + #endif //PORYGONLANG_SCRIPTTYPE_HPP diff --git a/src/Utilities/HashedString.hpp b/src/Utilities/HashedString.hpp index 58517a9..9cc8e2f 100644 --- a/src/Utilities/HashedString.hpp +++ b/src/Utilities/HashedString.hpp @@ -5,15 +5,17 @@ #include class HashedString{ - int _hash; - static unsigned constexpr const_hash(char const *input) { - return *input ? - static_cast(*input) + 33 * const_hash(input + 1) : - 5381; - } + const int _hash; public: - explicit HashedString(std::string s){ - _hash = const_hash(s.c_str()); + explicit HashedString(const std::string& s) : _hash(ConstHash(s.c_str())){ + } + explicit HashedString(char const *input) : _hash(ConstHash(input)){ + } + + static unsigned constexpr ConstHash(char const *input) { + return *input ? + static_cast(*input) + 33 * ConstHash(input + 1) : + 5381; } const int GetHash(){ diff --git a/tests/integration/Functions.cpp b/tests/integration/Functions.cpp new file mode 100644 index 0000000..cc9c16b --- /dev/null +++ b/tests/integration/Functions.cpp @@ -0,0 +1,17 @@ +#ifdef TESTS_BUILD +#include +#include "../src/Script.hpp" + +TEST_CASE( "Define script function", "[integration]" ) { + Script* script = Script::Create("function add(number a, number b) a + b end"); + REQUIRE(!script->Diagnostics -> HasErrors()); + script->Evaluate(); + auto variable = script->GetVariable("add"); + REQUIRE(variable != nullptr); + REQUIRE(variable->GetType()->GetClass() == TypeClass::Function); + delete script; +} + + + +#endif