Implements return statement
continuous-integration/drone/push Build is passing Details

This commit is contained in:
Deukhoofd 2019-06-07 15:23:13 +02:00
parent f143e526ab
commit f4a3918947
No known key found for this signature in database
GPG Key ID: B4C087AC81641654
12 changed files with 145 additions and 18 deletions

View File

@ -25,6 +25,7 @@ BoundStatement* Binder::BindStatement(ParsedStatement* statement){
case ParsedStatementKind ::Expression: return this -> BindExpressionStatement(statement);
case ParsedStatementKind::Assignment: return this -> BindAssignmentStatement(statement);
case ParsedStatementKind ::FunctionDeclaration: return this->BindFunctionDeclarationStatement(statement);
case ParsedStatementKind::Return: return this -> BindReturnStatement(statement);
case ParsedStatementKind::Bad: return new BoundBadStatement();
}
@ -105,6 +106,31 @@ BoundStatement *Binder::BindFunctionDeclarationStatement(ParsedStatement *statem
return new BoundBadStatement();
}
BoundStatement *Binder::BindReturnStatement(ParsedStatement* statement){
auto expression = ((ParsedReturnStatement*)statement)->GetExpression();
shared_ptr<ScriptType> currentReturnType;
if (this->_currentFunction == nullptr){
currentReturnType = this->_scriptData->GetReturnType();
} else{
currentReturnType = this->_currentFunction;
}
if (expression == nullptr && currentReturnType != nullptr){
this -> _scriptData -> Diagnostics -> LogError(DiagnosticCode::InvalidReturnType, statement->GetStartPosition(), statement->GetLength());
return new BoundBadStatement();
}
auto boundExpression = this->BindExpression(expression);
auto expresionType = boundExpression->GetType();
if (currentReturnType == nullptr){
currentReturnType.swap(expresionType);
return new BoundReturnStatement(boundExpression);
}
if (currentReturnType.get()->operator!=(expresionType.get())){
this -> _scriptData -> Diagnostics -> LogError(DiagnosticCode::InvalidReturnType, statement->GetStartPosition(), statement->GetLength());
return new BoundBadStatement();
}
return new BoundReturnStatement(boundExpression);
}
BoundExpression* Binder::BindExpression(ParsedExpression* expression){
switch (expression -> GetKind()){
case ParsedExpressionKind ::LiteralInteger:

View File

@ -10,6 +10,7 @@
class Binder {
Script* _scriptData;
BoundScope* _scope;
shared_ptr<FunctionScriptType> _currentFunction;
~Binder();
@ -18,6 +19,7 @@ class Binder {
BoundStatement *BindExpressionStatement(ParsedStatement *statement);
BoundStatement *BindAssignmentStatement(ParsedStatement *statement);
BoundStatement *BindFunctionDeclarationStatement(ParsedStatement * statement);
BoundStatement *BindReturnStatement(ParsedStatement *statement);
BoundExpression *BindExpression(ParsedExpression *expression);
BoundExpression *BindVariableExpression(VariableExpression *expression);

View File

@ -1,9 +1,8 @@
#include <utility>
#ifndef PORYGONLANG_BOUNDSTATEMENT_HPP
#define PORYGONLANG_BOUNDSTATEMENT_HPP
#include <utility>
#include <vector>
#include "../BoundExpressions/BoundExpression.hpp"
#include "../BoundVariables/BoundVariableKey.hpp"
@ -18,6 +17,7 @@ enum class BoundStatementKind{
Expression,
Assignment,
FunctionDeclaration,
Return,
};
class BoundStatement{
@ -116,6 +116,25 @@ public:
}
};
class BoundReturnStatement : public BoundStatement{
BoundExpression* _expression;
public:
explicit BoundReturnStatement(BoundExpression* expression){
_expression = expression;
}
~BoundReturnStatement() final{
delete _expression;
}
BoundStatementKind GetKind() final{
return BoundStatementKind ::Return;
}
BoundExpression* GetExpression(){
return _expression;
}
};
#include "BoundFunctionDeclarationStatement.hpp"
#endif //PORYGONLANG_BOUNDSTATEMENT_HPP

View File

@ -19,6 +19,7 @@ enum class DiagnosticCode{
ParameterCountMismatch,
ParameterTypeMismatch,
CantIndex,
InvalidReturnType,
};
#endif //PORYGONLANG_DIAGNOSTICCODE_HPP

View File

@ -1,8 +1,4 @@
#include <utility>
#include <utility>
#include <memory>
#include "Evaluator.hpp"
#include "EvaluationException.hpp"
@ -18,12 +14,15 @@ void Evaluator::Evaluate(BoundScriptStatement *statement) {
}
void Evaluator::EvaluateStatement(BoundStatement *statement) {
if (this->_hasReturned)
return;
switch (statement->GetKind()){
case BoundStatementKind ::Script: throw; // Should never happen
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::Return: return this -> EvaluateReturnStatement((BoundReturnStatement*)statement);
case BoundStatementKind::Bad:
throw;
@ -34,6 +33,8 @@ void Evaluator::EvaluateBlockStatement(BoundBlockStatement* statement) {
this->_evaluationScope->OuterScope();
for (auto s: statement->GetStatements()){
this -> EvaluateStatement(s);
if (this->_hasReturned)
break;
}
this->_evaluationScope->InnerScope();
}
@ -65,6 +66,16 @@ void Evaluator::EvaluateFunctionDeclarationStatement(BoundFunctionDeclarationSta
}
}
void Evaluator::EvaluateReturnStatement(BoundReturnStatement* statement){
auto expression = statement->GetExpression();
this->_hasReturned = true;
if (expression == nullptr){
return;
}
auto value = this -> EvaluateExpression(expression);
this -> _returnValue = value;
}
shared_ptr<EvalValue> Evaluator::EvaluateExpression(BoundExpression *expression) {
auto type = expression -> GetType();
switch (type->GetClass()){
@ -174,11 +185,13 @@ shared_ptr<EvalValue> Evaluator::EvaluateFunctionCallExpression(BoundExpression*
this->_evaluationScope->CreateVariable(key->GetScopeId(), key->GetIdentifier(), parameter->Clone());
}
this->EvaluateBlockStatement(function->GetInnerBlock().get());
return nullptr;
this->_hasReturned = false;
auto r = this -> _returnValue;
this -> _returnValue = nullptr;
return r;
}
EvalValue* Evaluator::EvaluateFunction(ScriptFunctionEvalValue *function, vector<EvalValue *> parameters) {
shared_ptr<EvalValue> Evaluator::EvaluateFunction(ScriptFunctionEvalValue *function, vector<EvalValue *> parameters) {
auto type = std::dynamic_pointer_cast<FunctionScriptType>(function->GetType());
auto parameterTypes = type->GetParameterTypes();
auto parameterKeys = type->GetParameterKeys();
@ -192,7 +205,10 @@ EvalValue* Evaluator::EvaluateFunction(ScriptFunctionEvalValue *function, vector
this->_evaluationScope->CreateVariable(key->GetScopeId(), key->GetIdentifier(), parameter->Clone());
}
this->EvaluateBlockStatement(function->GetInnerBlock().get());
return nullptr;
this->_hasReturned = false;
auto r = this -> _returnValue;
this -> _returnValue = nullptr;
return r;
}
shared_ptr<EvalValue> Evaluator::EvaluateIndexExpression(BoundExpression *expression) {

View File

@ -15,7 +15,8 @@
using namespace std;
class Evaluator {
shared_ptr<EvalValue> _result;
shared_ptr<EvalValue> _returnValue;
bool _hasReturned;
shared_ptr<EvalValue> _lastValue;
Script* _scriptData;
@ -26,6 +27,7 @@ class Evaluator {
void EvaluateExpressionStatement(BoundExpressionStatement* statement);
void EvaluateAssignmentStatement(BoundAssignmentStatement* statement);
void EvaluateFunctionDeclarationStatement(BoundFunctionDeclarationStatement *statement);
void EvaluateReturnStatement(BoundReturnStatement *statement);
shared_ptr<EvalValue> EvaluateExpression(BoundExpression* expression);
shared_ptr<NumericEvalValue> EvaluateIntegerExpression(BoundExpression* expression);
@ -47,6 +49,8 @@ class Evaluator {
public:
explicit Evaluator(Script* script){
_scriptData = script;
_hasReturned = false;
_returnValue = nullptr;
_evaluationScope = nullptr;
}
@ -55,7 +59,7 @@ public:
}
void Evaluate(BoundScriptStatement* statement);
EvalValue* EvaluateFunction(ScriptFunctionEvalValue* func, vector<EvalValue*> parameters);
shared_ptr<EvalValue> EvaluateFunction(ScriptFunctionEvalValue* func, vector<EvalValue*> parameters);
EvalValue* GetLastValue(){
return _lastValue.get();

View File

@ -15,7 +15,8 @@ enum class ParsedStatementKind{
Block,
Expression,
Assignment,
FunctionDeclaration
FunctionDeclaration,
Return
};
class ParsedStatement {
@ -166,4 +167,24 @@ public:
}
};
class ParsedReturnStatement : public ParsedStatement{
ParsedExpression* _expression;
public:
ParsedReturnStatement(ParsedExpression* expression, unsigned int start, unsigned int length) : ParsedStatement(start, length){
_expression = expression;
}
~ParsedReturnStatement() final{
delete _expression;
}
ParsedStatementKind GetKind() final{
return ParsedStatementKind ::Return;
}
ParsedExpression* GetExpression(){
return _expression;
}
};
#endif //PORYGONLANG_PARSEDSTATEMENT_HPP

View File

@ -33,6 +33,7 @@ ParsedStatement* Parser::ParseStatement(IToken* current){
switch (currentKind){
case TokenKind ::LocalKeyword: return this -> ParseAssignment(current);
case TokenKind ::FunctionKeyword: return this -> ParseFunctionDeclaration(current);
case TokenKind ::ReturnKeyword: return this->ParseReturnStatement(current);
default: break;
}
if (this->Peek()->GetKind() == TokenKind::AssignmentToken){
@ -134,7 +135,13 @@ ParsedStatement *Parser::ParseFunctionDeclaration(IToken *current) {
}
auto functionIdentifier = ((IdentifierToken*) functionIdentifierToken)->Value;
return new ParsedFunctionDeclarationStatement(HashedString(functionIdentifier), parameters, (ParsedBlockStatement*)block, start, block->GetEndPosition() - start);
}
ParsedStatement* Parser::ParseReturnStatement(IToken* current){
//TODO: if next token is on a different line, don't parse it as return expression.
auto expression = this->ParseExpression(this->Next());
auto start = current->GetStartPosition();
return new ParsedReturnStatement(expression, start, expression->GetEndPosition() - start);
}
ParsedExpression* Parser::ParseExpression(IToken* current){

View File

@ -29,6 +29,7 @@ class Parser {
ParsedStatement* ParseAssignment(IToken* current);
ParsedStatement *ParseBlock(const vector<TokenKind>& endTokens);
ParsedStatement* ParseFunctionDeclaration(IToken* current);
ParsedStatement *ParseReturnStatement(IToken *current);
ParsedExpression* ParseExpression(IToken* current);
ParsedExpression* ParseBinaryExpression(IToken* current, OperatorPrecedence parentPrecedence);

View File

@ -72,7 +72,7 @@ bool Script::HasFunction(const string &key) {
return f != _scriptVariables->end() && f.operator->()->second->GetType()->GetClass() == TypeClass ::Function;
}
EvalValue *Script::CallFunction(const string &key, vector<EvalValue *> variables) {
shared_ptr<EvalValue> Script::CallFunction(const string &key, vector<EvalValue *> variables) {
auto var = (ScriptFunctionEvalValue*)GetVariable(key);
return this->_evaluator->EvaluateFunction(var, std::move(variables));
}
@ -104,7 +104,7 @@ extern "C" {
EvalValue* CallFunction(Script* script, const char* key, EvalValue* parameters[], int parameterCount){
std::vector<EvalValue*> v(parameters, parameters + parameterCount);
return script->CallFunction(key, v);
return script->CallFunction(key, v).get();
}
}

View File

@ -20,6 +20,7 @@ class Script {
Evaluator* _evaluator;
unordered_map<int, shared_ptr<EvalValue>>* _scriptVariables;
BoundScriptStatement* _boundScript;
shared_ptr<ScriptType> _returnType;
explicit Script();
void Parse(string script);
@ -29,6 +30,10 @@ public:
~Script();
shared_ptr<ScriptType> GetReturnType(){
return _returnType;
}
void Evaluate();
EvalValue* GetLastValue();
@ -36,10 +41,8 @@ public:
EvalValue* GetVariable(const string& key);
bool HasVariable(const string& key);
EvalValue* CallFunction(const string& key, vector<EvalValue*> variables);
shared_ptr<EvalValue> CallFunction(const string& key, vector<EvalValue*> variables);
bool HasFunction(const string& key);
};

View File

@ -57,5 +57,32 @@ TEST_CASE( "Define script function and call from extern", "[integration]" ) {
delete script;
}
TEST_CASE( "Define script function and return", "[integration]" ) {
Script* script = Script::Create(
"val = 0\n"
"function add(number a, number b) \n"
"return a + b \n"
"val = val + 1\n"
"end");
REQUIRE(!script->Diagnostics -> HasErrors());
script->Evaluate();
REQUIRE(script->HasFunction("add"));
auto toAddVal = new IntegerEvalValue(5);
auto toAddVal2 = new IntegerEvalValue(6);
auto result = script->CallFunction("add", {toAddVal, toAddVal2});
delete toAddVal;
delete toAddVal2;
REQUIRE(result->GetType()->GetClass() == TypeClass::Number);
REQUIRE(result->EvaluateInteger() == 11);
auto variable = script->GetVariable("val");
REQUIRE(variable->GetType()->GetClass() == TypeClass::Number);
REQUIRE(variable->EvaluateInteger() == 0);
delete script;
}
#endif