From d949d9aa8faab2199f9c6a6f099ad8b9a3d7c236 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Thu, 23 May 2019 18:50:09 +0200 Subject: [PATCH] Work on evaluation --- CMakeLists.txt | 9 ++ .../BoundExpressions/BoundExpression.hpp | 13 +- src/Binder/BoundStatements/BoundStatement.hpp | 8 + src/Evaluator/BinaryEvaluation.cpp | 4 + src/Evaluator/EvaluationException.hpp | 22 +++ src/Evaluator/Evaluator.cpp | 151 ++++++++++++++++++ src/Evaluator/Evaluator.hpp | 39 +++++ src/Script.cpp | 9 ++ src/Script.hpp | 20 ++- .../numerical_operations_tests.cpp | 12 ++ 10 files changed, 283 insertions(+), 4 deletions(-) create mode 100644 src/Evaluator/BinaryEvaluation.cpp create mode 100644 src/Evaluator/EvaluationException.hpp create mode 100644 src/Evaluator/Evaluator.cpp create mode 100644 src/Evaluator/Evaluator.hpp create mode 100644 tests/integration/numerical_operations_tests.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1aa08a5..a8932f6 100755 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,4 +15,13 @@ add_executable(PorygonLangTests target_compile_definitions(PorygonLangTests PRIVATE TESTS_BUILD) +find_package( Boost ) + +include_directories( + ${BOOST_INCLUDE_DIRS} +) + +target_link_libraries(PorygonLang ${Boost_LIBRARIES} ) +target_link_libraries(PorygonLangTests ${Boost_LIBRARIES} ) + include(CTest) diff --git a/src/Binder/BoundExpressions/BoundExpression.hpp b/src/Binder/BoundExpressions/BoundExpression.hpp index e61e149..3425681 100644 --- a/src/Binder/BoundExpressions/BoundExpression.hpp +++ b/src/Binder/BoundExpressions/BoundExpression.hpp @@ -20,7 +20,6 @@ enum class BoundExpressionKind{ Unary, Binary, - Parenthesized, }; class BoundExpression{ @@ -150,6 +149,18 @@ public: BoundExpressionKind GetKind() final{ return BoundExpressionKind ::Binary; } + + BoundExpression* GetLeft(){ + return _left; + } + + BoundExpression* GetRight(){ + return _right; + } + + BoundBinaryOperation GetOperation(){ + return _operation; + } }; class BoundUnaryExpression : public BoundExpression { diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index 58b5b4c..9031a39 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -37,6 +37,10 @@ public: BoundStatementKind GetKind() override{ return BoundStatementKind ::Block; } + + vector GetStatements(){ + return _statements; + } }; class BoundScriptStatement : public BoundBlockStatement{ @@ -62,6 +66,10 @@ public: BoundStatementKind GetKind() final{ return BoundStatementKind ::Expression; } + + BoundExpression* GetExpression(){ + return _expression; + } }; #endif //PORYGONLANG_BOUNDSTATEMENT_HPP diff --git a/src/Evaluator/BinaryEvaluation.cpp b/src/Evaluator/BinaryEvaluation.cpp new file mode 100644 index 0000000..b556f64 --- /dev/null +++ b/src/Evaluator/BinaryEvaluation.cpp @@ -0,0 +1,4 @@ + +#include "EvaluationException.hpp" +#include "Evaluator.hpp" + diff --git a/src/Evaluator/EvaluationException.hpp b/src/Evaluator/EvaluationException.hpp new file mode 100644 index 0000000..bf62a48 --- /dev/null +++ b/src/Evaluator/EvaluationException.hpp @@ -0,0 +1,22 @@ + +#ifndef PORYGONLANG_EVALUATIONEXCEPTION_HPP +#define PORYGONLANG_EVALUATIONEXCEPTION_HPP +#include +#include +using namespace std; + +class EvaluationException : std::exception { + string _message; +public: + explicit EvaluationException(string message){ + _message = message; + } + + const string defaultErrorText = "An evaluation exception occurred: "; + const char* what() const noexcept final{ + return (defaultErrorText + _message).c_str(); + } +}; + + +#endif //PORYGONLANG_EVALUATIONEXCEPTION_HPP diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp new file mode 100644 index 0000000..96695c9 --- /dev/null +++ b/src/Evaluator/Evaluator.cpp @@ -0,0 +1,151 @@ + +#include "Evaluator.hpp" +#include "EvaluationException.hpp" +#include "BinaryEvaluation.cpp" +#include "../Script.hpp" + +void Evaluator::Evaluate(BoundScriptStatement *statement) { + EvaluateBlockStatement(statement); +} + +void Evaluator::EvaluateStatement(BoundStatement *statement) { + 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); + } +} + +void Evaluator::EvaluateBlockStatement(BoundBlockStatement* statement) { + for (auto s: statement->GetStatements()){ + this -> EvaluateStatement(s); + } +} + +void Evaluator::EvaluateExpressionStatement(BoundExpressionStatement *statement) { + this->_scriptData->_lastValue = this -> EvaluateExpression(statement->GetExpression()); +} + +any *Evaluator::EvaluateExpression(BoundExpression *expression) { + auto type = expression -> GetType(); + switch (type->GetClass()){ + case TypeClass ::Number: + { + auto numType = (NumericScriptType*)type; + if (numType->IsAwareOfFloat()){ + if (numType->IsFloat()){ + double d = this -> EvaluateFloatExpression(expression); + return new boost::any(d); + } else{ + long l = this -> EvaluateIntegerExpression(expression); + return new boost::any(l); + } + } + break; + } + } +} + +long Evaluator::EvaluateIntegerExpression(BoundExpression *expression) { + auto exprType = expression->GetType(); + if (exprType->GetClass() != TypeClass::Number){ + throw EvaluationException("Can't evaluate expression as integer, it will not return a number."); + } + auto numType = (NumericScriptType*)exprType; + if (numType->IsAwareOfFloat() && numType->IsFloat()){ + throw EvaluationException("Can't evaluate expression as integer, it will return a float, not an integer."); + } + switch (expression->GetKind()){ + case BoundExpressionKind ::LiteralInteger: return ((BoundLiteralIntegerExpression*)expression)->GetValue(); + case BoundExpressionKind ::Binary: return this -> EvaluateIntegerBinary((BoundBinaryExpression*)expression); + + case BoundExpressionKind ::LiteralFloat: + case BoundExpressionKind ::LiteralString: + case BoundExpressionKind ::LiteralBool: + case BoundExpressionKind ::Bad: + throw; + } +} + +double Evaluator::EvaluateFloatExpression(BoundExpression *expression) { + return 0; +} + +bool Evaluator::EvaluateBoolExpression(BoundExpression *expression) { + return false; +} + +std::string Evaluator::EvaluateStringExpression(BoundExpression *expression) { + return std::__cxx11::string(); +} + +long Evaluator::EvaluateIntegerBinary(BoundBinaryExpression *expression) { + long leftValue = this -> EvaluateIntegerExpression(expression->GetLeft()); + long rightValue = this -> EvaluateIntegerExpression(expression->GetRight()); + + switch (expression->GetOperation()){ + case BoundBinaryOperation ::Addition: return leftValue + rightValue; + case BoundBinaryOperation ::Subtraction: return leftValue - rightValue; + case BoundBinaryOperation ::Multiplication: return leftValue * rightValue; + case BoundBinaryOperation ::Division: return leftValue / rightValue; + + default: + throw EvaluationException("Can't evaluate operation to integer"); + } +} + +double EvaluateBinaryOperation(double l, double r, BoundBinaryOperation op){ + switch (op){ + case BoundBinaryOperation ::Addition: return l + r; + case BoundBinaryOperation ::Subtraction: return l - r; + case BoundBinaryOperation ::Multiplication: return l * r; + case BoundBinaryOperation ::Division: return l / r; + + default: + throw EvaluationException("Can't evaluate operation to float"); + } +} +double EvaluateBinaryOperation(double l, long r, BoundBinaryOperation op){ + switch (op){ + case BoundBinaryOperation ::Addition: return l + r; + case BoundBinaryOperation ::Subtraction: return l - r; + case BoundBinaryOperation ::Multiplication: return l * r; + case BoundBinaryOperation ::Division: return l / r; + + default: + throw EvaluationException("Can't evaluate operation to float"); + } +} +double EvaluateBinaryOperation(long l, double r, BoundBinaryOperation op){ + switch (op){ + case BoundBinaryOperation ::Addition: return l + r; + case BoundBinaryOperation ::Subtraction: return l - r; + case BoundBinaryOperation ::Multiplication: return l * r; + case BoundBinaryOperation ::Division: return l / r; + + default: + throw EvaluationException("Can't evaluate operation to float"); + } +} + +double Evaluator::EvaluateFloatBinary(BoundBinaryExpression *expression) { + auto left = expression->GetLeft(); + auto right = expression->GetRight(); + auto leftType = (NumericScriptType*)left->GetType(); + auto rightType = (NumericScriptType*)right->GetType(); + if (leftType->IsFloat()){ + double leftValue = this -> EvaluateFloatExpression(left); + if (rightType->IsFloat()){ + double rightValue = this -> EvaluateFloatExpression(right); + return EvaluateBinaryOperation(leftValue, rightValue, expression->GetOperation()); + } else{ + long rightValue = this -> EvaluateIntegerExpression(right); + return EvaluateBinaryOperation(leftValue, rightValue, expression->GetOperation()); + } + } else{ + long leftValue = this-> EvaluateIntegerExpression(left); + // If the left is an integer, we know the right must be a float, otherwise we'd be evaluating as integer; + double rightValue = this -> EvaluateFloatExpression(right); + return EvaluateBinaryOperation(leftValue, rightValue, expression->GetOperation()); + } +} \ No newline at end of file diff --git a/src/Evaluator/Evaluator.hpp b/src/Evaluator/Evaluator.hpp new file mode 100644 index 0000000..6f6e193 --- /dev/null +++ b/src/Evaluator/Evaluator.hpp @@ -0,0 +1,39 @@ + +#ifndef PORYGONLANG_EVALUATOR_HPP +#define PORYGONLANG_EVALUATOR_HPP + +#include +#include +#include "../Binder/BoundStatements/BoundStatement.hpp" +#include "../Script.hpp" + +using namespace boost; + +class Evaluator { + any* _result; + + Script* _scriptData; + + void EvaluateStatement(BoundStatement* statement); + void EvaluateBlockStatement(BoundBlockStatement* statement); + void EvaluateExpressionStatement(BoundExpressionStatement* statement); + + any* EvaluateExpression(BoundExpression* expression); + long EvaluateIntegerExpression(BoundExpression* expression); + double EvaluateFloatExpression(BoundExpression* expression); + bool EvaluateBoolExpression(BoundExpression* expression); + std::string EvaluateStringExpression(BoundExpression* expression); + + long EvaluateIntegerBinary(BoundBinaryExpression* expression); + double EvaluateFloatBinary(BoundBinaryExpression *expression); +public: + Evaluator(Script* script){ + _scriptData = script; + } + void Evaluate(BoundScriptStatement* statement); + +}; + + + +#endif //PORYGONLANG_EVALUATOR_HPP diff --git a/src/Script.cpp b/src/Script.cpp index b178512..a130a1f 100644 --- a/src/Script.cpp +++ b/src/Script.cpp @@ -12,6 +12,15 @@ Script Script::Create(string script) { return s; } +Script::Script() { + Diagnostics = new class Diagnostics(); + _evaluator = new Evaluator(this); +} + +void Script::Evaluate() { + _evaluator->Evaluate(BoundScript); +} + Script::~Script() { delete this -> Diagnostics; delete this -> BoundScript; diff --git a/src/Script.hpp b/src/Script.hpp index 61deb63..de80f39 100644 --- a/src/Script.hpp +++ b/src/Script.hpp @@ -5,15 +5,23 @@ #define PORYGONLANG_SCRIPT_HPP #include +#include #include "Diagnostics/Diagnostics.hpp" #include "Binder/BoundStatements/BoundStatement.hpp" +class Script; +class Evaluator; +#include "Evaluator/Evaluator.hpp" using namespace std; class Script { - explicit Script(){ - Diagnostics = new class Diagnostics(); - }; + friend class Evaluator; + + boost::any* _lastValue; + + Evaluator* _evaluator; + + explicit Script(); void Parse(string script); BoundScriptStatement* BoundScript; @@ -22,6 +30,12 @@ public: Diagnostics* Diagnostics; ~Script(); + + void Evaluate(); + + boost::any* GetLastValue(){ + return _lastValue; + }; }; diff --git a/tests/integration/numerical_operations_tests.cpp b/tests/integration/numerical_operations_tests.cpp new file mode 100644 index 0000000..18e7c5f --- /dev/null +++ b/tests/integration/numerical_operations_tests.cpp @@ -0,0 +1,12 @@ +#ifdef TESTS_BUILD +#include +#include "../src/Script.hpp" + +TEST_CASE( "Simple addition", "[integration]" ) { + Script script = Script::Create("1 + 5"); + REQUIRE(!script.Diagnostics -> HasErrors()); + script.Evaluate(); + auto lastValue = script.GetLastValue(); + REQUIRE(*any_cast(lastValue) == 6); +} +#endif