Implements binding and evaluating function declarations
This commit is contained in:
parent
c407ba2f50
commit
6936b26cae
|
@ -24,6 +24,7 @@ BoundStatement* Binder::BindStatement(ParsedStatement* statement){
|
||||||
case ParsedStatementKind ::Block: return this -> BindBlockStatement(statement);
|
case ParsedStatementKind ::Block: return this -> BindBlockStatement(statement);
|
||||||
case ParsedStatementKind ::Expression: return this -> BindExpressionStatement(statement);
|
case ParsedStatementKind ::Expression: return this -> BindExpressionStatement(statement);
|
||||||
case ParsedStatementKind::Assignment: return this -> BindAssignmentStatement(statement);
|
case ParsedStatementKind::Assignment: return this -> BindAssignmentStatement(statement);
|
||||||
|
case ParsedStatementKind ::FunctionDeclaration: return this->BindFunctionDeclarationStatement(statement);
|
||||||
|
|
||||||
case ParsedStatementKind::Bad: return new BoundBadStatement();
|
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<ScriptType*> parameterTypes = vector<ScriptType*>(parameters.size());
|
||||||
|
vector<int> parameterKeys = vector<int>(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){
|
BoundExpression* Binder::BindExpression(ParsedExpression* expression){
|
||||||
switch (expression -> GetKind()){
|
switch (expression -> GetKind()){
|
||||||
case ParsedExpressionKind ::LiteralInteger:
|
case ParsedExpressionKind ::LiteralInteger:
|
||||||
|
@ -226,3 +262,4 @@ BoundExpression* Binder::BindUnaryOperator(UnaryExpression* expression){
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ class Binder {
|
||||||
BoundStatement *BindBlockStatement(ParsedStatement *statement);
|
BoundStatement *BindBlockStatement(ParsedStatement *statement);
|
||||||
BoundStatement *BindExpressionStatement(ParsedStatement *statement);
|
BoundStatement *BindExpressionStatement(ParsedStatement *statement);
|
||||||
BoundStatement *BindAssignmentStatement(ParsedStatement *statement);
|
BoundStatement *BindAssignmentStatement(ParsedStatement *statement);
|
||||||
|
BoundStatement *BindFunctionDeclarationStatement(ParsedStatement * statement);
|
||||||
|
|
||||||
BoundExpression *BindExpression(ParsedExpression *expression);
|
BoundExpression *BindExpression(ParsedExpression *expression);
|
||||||
BoundExpression *BindVariableExpression(VariableExpression *expression);
|
BoundExpression *BindVariableExpression(VariableExpression *expression);
|
||||||
|
|
|
@ -16,6 +16,7 @@ enum class BoundStatementKind{
|
||||||
Block,
|
Block,
|
||||||
Expression,
|
Expression,
|
||||||
Assignment,
|
Assignment,
|
||||||
|
FunctionDeclaration,
|
||||||
};
|
};
|
||||||
|
|
||||||
class BoundStatement{
|
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
|
#endif //PORYGONLANG_BOUNDSTATEMENT_HPP
|
||||||
|
|
|
@ -32,6 +32,10 @@ public:
|
||||||
int GetDeepestScope(){
|
int GetDeepestScope(){
|
||||||
return _deepestScope;
|
return _deepestScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int GetCurrentScope(){
|
||||||
|
return _currentScope;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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<EvalValue*>& 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
|
|
@ -3,6 +3,7 @@
|
||||||
#include "EvaluationException.hpp"
|
#include "EvaluationException.hpp"
|
||||||
#include "../Script.hpp"
|
#include "../Script.hpp"
|
||||||
#include "EvaluationScope/EvaluationScope.hpp"
|
#include "EvaluationScope/EvaluationScope.hpp"
|
||||||
|
#include "EvalValues/ScriptFunctionEvalValue.hpp"
|
||||||
|
|
||||||
void Evaluator::Evaluate(BoundScriptStatement *statement) {
|
void Evaluator::Evaluate(BoundScriptStatement *statement) {
|
||||||
this->_evaluationScope = new EvaluationScope(this->_scriptData->_scriptVariables, statement->GetDeepestScope());
|
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 ::Block: return this -> EvaluateBlockStatement((BoundBlockStatement*)statement);
|
||||||
case BoundStatementKind ::Expression: return this -> EvaluateExpressionStatement((BoundExpressionStatement*)statement);
|
case BoundStatementKind ::Expression: return this -> EvaluateExpressionStatement((BoundExpressionStatement*)statement);
|
||||||
case BoundStatementKind ::Assignment: return this -> EvaluateAssignmentStatement((BoundAssignmentStatement*)statement);
|
case BoundStatementKind ::Assignment: return this -> EvaluateAssignmentStatement((BoundAssignmentStatement*)statement);
|
||||||
|
case BoundStatementKind ::FunctionDeclaration: return this->EvaluateFunctionDeclarationStatement((BoundFunctionDeclarationStatement*)statement);
|
||||||
|
|
||||||
case BoundStatementKind::Bad:
|
case BoundStatementKind::Bad:
|
||||||
throw;
|
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) {
|
EvalValue *Evaluator::EvaluateExpression(BoundExpression *expression) {
|
||||||
auto type = expression -> GetType();
|
auto type = expression -> GetType();
|
||||||
switch (type->GetClass()){
|
switch (type->GetClass()){
|
||||||
|
|
|
@ -20,9 +20,9 @@ class Evaluator {
|
||||||
EvaluationScope* _evaluationScope;
|
EvaluationScope* _evaluationScope;
|
||||||
|
|
||||||
void EvaluateStatement(BoundStatement* statement);
|
void EvaluateStatement(BoundStatement* statement);
|
||||||
void EvaluateBlockStatement(BoundBlockStatement* statement);
|
|
||||||
void EvaluateExpressionStatement(BoundExpressionStatement* statement);
|
void EvaluateExpressionStatement(BoundExpressionStatement* statement);
|
||||||
void EvaluateAssignmentStatement(BoundAssignmentStatement* statement);
|
void EvaluateAssignmentStatement(BoundAssignmentStatement* statement);
|
||||||
|
void EvaluateFunctionDeclarationStatement(BoundFunctionDeclarationStatement *statement);
|
||||||
|
|
||||||
EvalValue* EvaluateExpression(BoundExpression* expression);
|
EvalValue* EvaluateExpression(BoundExpression* expression);
|
||||||
NumericEvalValue* EvaluateIntegerExpression(BoundExpression* expression);
|
NumericEvalValue* EvaluateIntegerExpression(BoundExpression* expression);
|
||||||
|
@ -50,7 +50,11 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
void Evaluate(BoundScriptStatement* statement);
|
void Evaluate(BoundScriptStatement* statement);
|
||||||
|
void EvaluateBlockStatement(BoundBlockStatement* statement);
|
||||||
|
|
||||||
|
EvaluationScope* GetScope(){
|
||||||
|
return _evaluationScope;
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -37,3 +37,4 @@ BooleanEvalValue *Evaluator::EvaluateBooleanUnary(BoundUnaryExpression *expressi
|
||||||
throw;
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
|
||||||
#ifndef PORYGONLANG_SCRIPTTYPE_HPP
|
#ifndef PORYGONLANG_SCRIPTTYPE_HPP
|
||||||
#define PORYGONLANG_SCRIPTTYPE_HPP
|
#define PORYGONLANG_SCRIPTTYPE_HPP
|
||||||
|
|
||||||
|
#include <utility>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
enum class TypeClass{
|
enum class TypeClass{
|
||||||
Error,
|
Error,
|
||||||
Nil,
|
Nil,
|
||||||
|
@ -55,4 +57,41 @@ public:
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class FunctionScriptType : public ScriptType{
|
||||||
|
ScriptType* _returnType;
|
||||||
|
std::vector<ScriptType*> _parameterTypes;
|
||||||
|
std::vector<int> _parameterKeys;
|
||||||
|
int _scopeId;
|
||||||
|
public:
|
||||||
|
FunctionScriptType(ScriptType* returnType, std::vector<ScriptType*> parameterTypes, std::vector<int> 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<ScriptType*> GetParameterTypes(){
|
||||||
|
return _parameterTypes;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<int> GetParameterKeys(){
|
||||||
|
return _parameterKeys;
|
||||||
|
}
|
||||||
|
|
||||||
|
int GetScopeId(){
|
||||||
|
return _scopeId;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
#endif //PORYGONLANG_SCRIPTTYPE_HPP
|
#endif //PORYGONLANG_SCRIPTTYPE_HPP
|
||||||
|
|
|
@ -5,15 +5,17 @@
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
class HashedString{
|
class HashedString{
|
||||||
int _hash;
|
const int _hash;
|
||||||
static unsigned constexpr const_hash(char const *input) {
|
|
||||||
return *input ?
|
|
||||||
static_cast<unsigned int>(*input) + 33 * const_hash(input + 1) :
|
|
||||||
5381;
|
|
||||||
}
|
|
||||||
public:
|
public:
|
||||||
explicit HashedString(std::string s){
|
explicit HashedString(const std::string& s) : _hash(ConstHash(s.c_str())){
|
||||||
_hash = const_hash(s.c_str());
|
}
|
||||||
|
explicit HashedString(char const *input) : _hash(ConstHash(input)){
|
||||||
|
}
|
||||||
|
|
||||||
|
static unsigned constexpr ConstHash(char const *input) {
|
||||||
|
return *input ?
|
||||||
|
static_cast<unsigned int>(*input) + 33 * ConstHash(input + 1) :
|
||||||
|
5381;
|
||||||
}
|
}
|
||||||
|
|
||||||
const int GetHash(){
|
const int GetHash(){
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
#ifdef TESTS_BUILD
|
||||||
|
#include <catch.hpp>
|
||||||
|
#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
|
Loading…
Reference in New Issue