From d21cfeaac8a418a23110dfb63ca0b3d0b5294cb9 Mon Sep 17 00:00:00 2001 From: Deukhoofd Date: Mon, 2 Sep 2019 20:48:52 +0200 Subject: [PATCH] Added support for creating a string outline of a bound script for debugging purposes --- .../BoundExpressions/BoundExpression.hpp | 130 +++++++++++++++++ .../BoundFunctionCallExpression.hpp | 14 ++ .../BoundRequireExpression.hpp | 6 + .../BoundExpressions/BoundTableExpression.hpp | 8 ++ .../BoundFunctionDeclarationStatement.hpp | 12 ++ src/Binder/BoundStatements/BoundStatement.hpp | 131 ++++++++++++++++++ src/Evaluator/Evaluator.cpp | 1 + src/ScriptTypes/ScriptType.hpp | 21 +++ src/StandardLibraries/BasicLibrary.hpp | 1 + tests/TreeStringTests.cpp | 29 ++++ tests/integration/EqualityOperationsTests.cpp | 17 +++ 11 files changed, 370 insertions(+) create mode 100644 tests/TreeStringTests.cpp diff --git a/src/Binder/BoundExpressions/BoundExpression.hpp b/src/Binder/BoundExpressions/BoundExpression.hpp index a6ab0e0..1ce25d3 100644 --- a/src/Binder/BoundExpressions/BoundExpression.hpp +++ b/src/Binder/BoundExpressions/BoundExpression.hpp @@ -7,6 +7,7 @@ #include "../../ScriptTypes/ScriptType.hpp" #include "../BoundOperators.hpp" #include "../BoundVariables/BoundVariableKey.hpp" +#include "../../Utilities/StringUtils.hpp" using namespace std; @@ -62,6 +63,8 @@ namespace Porygon::Binder { inline unsigned int GetLength() const { return _length; } + + virtual void GetTreeString(std::stringstream& stream, size_t indents) const = 0; }; class BoundBadExpression : public BoundExpression { @@ -74,6 +77,12 @@ namespace Porygon::Binder { inline BoundExpressionKind GetKind() const final { return BoundExpressionKind::Bad; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "BadExpression"; + } }; class BoundLiteralIntegerExpression : public BoundExpression { @@ -93,6 +102,12 @@ namespace Porygon::Binder { inline int64_t GetValue() const { return _value; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "LiteralInteger: " << _value << " (" << GetType()->ToString() << ")"; + } }; class BoundLiteralFloatExpression : public BoundExpression { @@ -112,6 +127,12 @@ namespace Porygon::Binder { inline double GetValue() const { return _value; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "LiteralFloat: " << _value << " (" << GetType()->ToString() << ")"; + } }; class BoundLiteralStringExpression : public BoundExpression { @@ -132,6 +153,13 @@ namespace Porygon::Binder { inline const u16string* GetValue() const { return &_value; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "LiteralString: " << Utilities::StringUtils::FromUTF8(_value) + << " (" << GetType()->ToString() << ")"; + } }; class BoundLiteralBoolExpression : public BoundExpression { @@ -151,6 +179,12 @@ namespace Porygon::Binder { inline bool GetValue() const { return _value; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "LiteralBool: " << _value << " (" << GetType()->ToString() << ")"; + } }; class BoundNilExpression : public BoundExpression { @@ -163,6 +197,12 @@ namespace Porygon::Binder { inline BoundExpressionKind GetKind() const final { return BoundExpressionKind::Nil; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "NilExpression" << " (" << GetType()->ToString() << ")"; + } }; class BoundVariableExpression : public BoundExpression { @@ -187,6 +227,13 @@ namespace Porygon::Binder { inline const BoundVariableKey *GetKey() const { return _key; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "VariableExpression: " << _key->GetIdentifier()->GetString().get() + << " (" << GetType()->ToString() << ")"; + } }; class BoundBinaryExpression : public BoundExpression { @@ -227,6 +274,35 @@ namespace Porygon::Binder { inline BoundBinaryOperation GetOperation() const { return _operation; } + + static std::string GetOperationString(BoundBinaryOperation op){ + switch (op){ + + case BoundBinaryOperation::Addition: return "addition"; + case BoundBinaryOperation::Subtraction: return "subtraction"; + case BoundBinaryOperation::Multiplication: return "multiplication"; + case BoundBinaryOperation::Division: return "division"; + case BoundBinaryOperation::Equality: return "equality"; + case BoundBinaryOperation::Inequality: return "inequality"; + case BoundBinaryOperation::LessThan: return "lessThan"; + case BoundBinaryOperation::LessThanEquals: return "lessThanEquals"; + case BoundBinaryOperation::GreaterThan: return "greaterThan"; + case BoundBinaryOperation::GreaterThanEquals: return "greaterThanEquals"; + case BoundBinaryOperation::LogicalAnd: return "logicalAnd"; + case BoundBinaryOperation::LogicalOr: return "logicalOr"; + case BoundBinaryOperation::Concatenation: return "concatenation"; + } + throw exception(); + } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "BinaryExpression: " << GetOperationString(_operation) + << " (" << GetType()->ToString() << ")" << endl; + _left->GetTreeString(stream, indents + 1); + _right->GetTreeString(stream, indents + 1); + } }; class BoundUnaryExpression : public BoundExpression { @@ -258,6 +334,22 @@ namespace Porygon::Binder { inline BoundUnaryOperation GetOperation() const { return _operation; } + + static std::string GetOperationString(BoundUnaryOperation op){ + switch (op){ + case BoundUnaryOperation::Negation: return "negation"; + case BoundUnaryOperation::LogicalNegation: return "logicalNegation"; + } + throw exception(); + } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "UnaryExpression: " << GetOperationString(_operation) + << " (" << GetType()->ToString() << ")" << endl; + _operand->GetTreeString(stream, indents + 1); + } }; class BoundIndexExpression : public BoundExpression { @@ -289,6 +381,16 @@ namespace Porygon::Binder { inline const BoundExpression *GetIndexExpression() const { return _indexExpression; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "IndexExpression" << " (" << GetType()->ToString() << ")" << endl; + _indexableExpression->GetTreeString(stream, indents + 1); + stream << endl; + _indexExpression->GetTreeString(stream, indents + 1); + } + }; class BoundPeriodIndexExpression : public BoundExpression { @@ -319,6 +421,15 @@ namespace Porygon::Binder { inline const Utilities::HashedString* GetIndex() const { return &_index; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "PeriodIndex: " << _index.GetString().get() << " (" << GetType()->ToString() << ")" << endl; + stream << endl; + _indexableExpression->GetTreeString(stream, indents + 1); + } + }; class BoundNumericalTableExpression : public BoundExpression { @@ -344,6 +455,17 @@ namespace Porygon::Binder { inline const vector *GetExpressions() const { return &_expressions; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "NumericalTable" << " (" << GetType()->ToString() << ")" << endl; + for (auto _expression : _expressions){ + stream << endl; + _expression->GetTreeString(stream, indents + 1); + } + } + }; class BoundCastExpression : public BoundExpression { @@ -366,6 +488,14 @@ namespace Porygon::Binder { inline BoundExpressionKind GetKind() const final { return BoundExpressionKind::Cast; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "CastExpression" << " (" << GetType()->ToString() << ")" << endl; + _expression->GetTreeString(stream, indents + 1); + } + }; } diff --git a/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp b/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp index 19f94c8..ef7c287 100644 --- a/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp +++ b/src/Binder/BoundExpressions/BoundFunctionCallExpression.hpp @@ -42,6 +42,20 @@ namespace Porygon::Binder { inline const Porygon::GenericFunctionOption *GetFunctionOption() const { return _option; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Function Call" << " (" << GetType()->ToString() << ")" << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + if (!_parameters.empty()){ + stream << "Parameters: " << endl; + for (auto p : _parameters){ + p->GetTreeString(stream, indents + 1); + } + } + } }; } diff --git a/src/Binder/BoundExpressions/BoundRequireExpression.hpp b/src/Binder/BoundExpressions/BoundRequireExpression.hpp index 0f7702f..dae0da9 100644 --- a/src/Binder/BoundExpressions/BoundRequireExpression.hpp +++ b/src/Binder/BoundExpressions/BoundRequireExpression.hpp @@ -26,6 +26,12 @@ namespace Porygon::Binder { inline BoundExpressionKind GetKind() const final { return BoundExpressionKind::Require; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Require" << " (" << GetType()->ToString() << ")"; + } }; } diff --git a/src/Binder/BoundExpressions/BoundTableExpression.hpp b/src/Binder/BoundExpressions/BoundTableExpression.hpp index 547e913..2e08510 100644 --- a/src/Binder/BoundExpressions/BoundTableExpression.hpp +++ b/src/Binder/BoundExpressions/BoundTableExpression.hpp @@ -29,6 +29,14 @@ namespace Porygon::Binder { inline const BoundBlockStatement *GetBlock() const { return _block; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "TableExpression" << " (" << GetType()->ToString() << ")" << endl; + _block->GetTreeString(stream, indents + 1); + } + }; } diff --git a/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp b/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp index 939a4de..bedf93c 100644 --- a/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp +++ b/src/Binder/BoundStatements/BoundFunctionDeclarationStatement.hpp @@ -40,6 +40,18 @@ namespace Porygon::Binder { inline std::shared_ptr GetType() const { return _type; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "FunctionDeclaration" << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Key: " << _key->GetIdentifier()->GetString().get() << endl; + stream << "Type: " << _type->ToString() << endl; + _block->GetTreeString(stream, indents + 1); + } + }; } diff --git a/src/Binder/BoundStatements/BoundStatement.hpp b/src/Binder/BoundStatements/BoundStatement.hpp index b0e5534..ce61d7e 100644 --- a/src/Binder/BoundStatements/BoundStatement.hpp +++ b/src/Binder/BoundStatements/BoundStatement.hpp @@ -32,6 +32,8 @@ namespace Porygon::Binder { virtual BoundStatementKind GetKind() const = 0; virtual ~BoundStatement() = default; + + virtual void GetTreeString(std::stringstream& stream, size_t indents) const = 0; }; class BoundBadStatement : public BoundStatement { @@ -40,6 +42,12 @@ namespace Porygon::Binder { inline BoundStatementKind GetKind() const final { return BoundStatementKind::Bad; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "BadStatement"; + } }; class BoundBreakStatement : public BoundStatement { @@ -48,6 +56,12 @@ namespace Porygon::Binder { inline BoundStatementKind GetKind() const final { return BoundStatementKind::Break; } + + void GetTreeString(std::stringstream& stream, size_t indents) const final{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "BreakStatement"; + } }; class BoundBlockStatement : public BoundStatement { @@ -72,6 +86,16 @@ namespace Porygon::Binder { inline const vector *GetStatements() const { return &_statements; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "BlockStatement"; + for (auto s : _statements){ + stream << endl; + s->GetTreeString(stream, indents + 1); + } + } }; class BoundScriptStatement : public BoundBlockStatement { @@ -114,6 +138,13 @@ namespace Porygon::Binder { inline const BoundExpression *GetExpression() const { return _expression; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "ExpressionStatement" << endl; + _expression->GetTreeString(stream, indents + 1); + } }; class BoundAssignmentStatement : public BoundStatement { @@ -143,6 +174,13 @@ namespace Porygon::Binder { inline const BoundExpression *GetExpression() const { return _expression; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Assignment -> " << _key->GetIdentifier()->GetString() << endl; + _expression->GetTreeString(stream, indents + 1); + } }; class BoundIndexAssignmentStatement : public BoundStatement { @@ -172,6 +210,15 @@ namespace Porygon::Binder { inline const BoundExpression *GetValueExpression() const { return _valueExpression; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "IndexAssignment" << endl; + _indexExpression->GetTreeString(stream, indents + 1); + stream << endl; + _valueExpression->GetTreeString(stream, indents + 1); + } }; class BoundReturnStatement : public BoundStatement { @@ -194,6 +241,16 @@ namespace Porygon::Binder { inline const BoundExpression *GetExpression() const { return _expression; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "ReturnStatement" << endl; + if (_expression != nullptr){ + _expression->GetTreeString(stream, indents + 1); + } + } + }; class BoundConditionalStatement : public BoundStatement { @@ -230,6 +287,26 @@ namespace Porygon::Binder { inline const BoundStatement *GetElseStatement() const { return _elseStatement; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "ConditionalStatement" << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Condition:" << endl; + _condition->GetTreeString(stream, indents + 1); + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "If True:" << endl; + _block->GetTreeString(stream, indents + 1); + if (_elseStatement != nullptr){ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Else:" << endl; + _elseStatement->GetTreeString(stream, indents + 1); + } + } }; class BoundNumericalForStatement : public BoundStatement { @@ -283,6 +360,30 @@ namespace Porygon::Binder { inline const BoundStatement* GetBlock() const{ return _block; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "NumericForLoopStatement" << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Start:" << endl; + _start->GetTreeString(stream, indents + 1); + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "End:" << endl; + _end->GetTreeString(stream, indents + 1); + if (_step != nullptr){ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Step:" << endl; + _step->GetTreeString(stream, indents + 1); + } + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Do:" << endl; + _block->GetTreeString(stream, indents + 1); + } }; class BoundGenericForStatement : public BoundStatement { @@ -329,6 +430,22 @@ namespace Porygon::Binder { inline const BoundStatement* GetBlock() const{ return _block; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "GenericForLoopStatement" << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Key: " << _keyIdentifier->GetIdentifier()->GetString().get() << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Value: " << _valueIdentifier->GetIdentifier()->GetString().get() << endl; + stream << "Iterator: " << endl; + _iterator->GetTreeString(stream, indents + 1); + stream << endl; + _block->GetTreeString(stream, indents + 1); + } }; class BoundWhileStatement : public BoundStatement { @@ -358,6 +475,20 @@ namespace Porygon::Binder { inline const BoundStatement* GetBlock() const{ return _block; } + + void GetTreeString(std::stringstream& stream, size_t indents) const override{ + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "ConditionalStatement" << endl; + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "Condition:" << endl; + _condition->GetTreeString(stream, indents + 1); + for (size_t i = 0; i < indents; i++) + stream << "\t"; + stream << "While True:" << endl; + _block->GetTreeString(stream, indents + 1); + } }; } diff --git a/src/Evaluator/Evaluator.cpp b/src/Evaluator/Evaluator.cpp index e7c6ea5..9bf54eb 100644 --- a/src/Evaluator/Evaluator.cpp +++ b/src/Evaluator/Evaluator.cpp @@ -263,6 +263,7 @@ namespace Porygon::Evaluation { case BoundExpressionKind::Cast: return this -> EvaluateImplicitCastExpression(expression); } + throw exception(); } EvalValuePointer Evaluator::EvaluateBinary(const BoundBinaryExpression *expression){ diff --git a/src/ScriptTypes/ScriptType.hpp b/src/ScriptTypes/ScriptType.hpp index bf8ad54..3d65971 100644 --- a/src/ScriptTypes/ScriptType.hpp +++ b/src/ScriptTypes/ScriptType.hpp @@ -26,6 +26,7 @@ namespace Porygon{ All, }; + class ScriptType{ TypeClass _class; public: @@ -84,6 +85,26 @@ namespace Porygon{ return CastResult::InvalidCast; return CastResult::InvalidCast; } + + static std::string ToString(TypeClass c){ + switch (c){ + + case TypeClass::Error: return "error"; + case TypeClass::Nil: return "nil"; + case TypeClass::Number: return "number"; + case TypeClass::Bool: return "bool"; + case TypeClass::String: return "string"; + case TypeClass::Function: return "function"; + case TypeClass::UserData: return "userdata"; + case TypeClass::Table: return "table"; + case TypeClass::All: return "all"; + } + throw exception(); + } + + [[nodiscard]] virtual std::string ToString() const{ + return ToString(this->_class); + } }; class NumericScriptType : public ScriptType{ diff --git a/src/StandardLibraries/BasicLibrary.hpp b/src/StandardLibraries/BasicLibrary.hpp index 3cf0d8a..f0e1cfa 100644 --- a/src/StandardLibraries/BasicLibrary.hpp +++ b/src/StandardLibraries/BasicLibrary.hpp @@ -110,6 +110,7 @@ namespace Porygon::StandardLibraries{ case TypeClass::Table: return new Evaluation::StringEvalValue(u"table"); case TypeClass::All: return new Evaluation::StringEvalValue(u"all"); } + throw exception(); } static shared_ptr GetTypeFuncType(){ return GetFuncType(StringScriptType::Dynamic,{{make_shared(TypeClass::All)}}); diff --git a/tests/TreeStringTests.cpp b/tests/TreeStringTests.cpp new file mode 100644 index 0000000..ebf745b --- /dev/null +++ b/tests/TreeStringTests.cpp @@ -0,0 +1,29 @@ +#ifdef TESTS_BUILD +#include +#include + +#include "../src/Binder/BoundStatements/BoundStatement.hpp" +using namespace Porygon::Binder; + +TEST_CASE( "Bad Statement To String", "[BoundTreeString]" ) { + std::stringstream stream; + auto s = new BoundBadStatement(); + s->GetTreeString(stream, 1); + REQUIRE(stream.str() == "\tBadStatement"); +} + +TEST_CASE( "Break Statement To String", "[BoundTreeString]" ) { + std::stringstream stream; + auto s = new BoundBreakStatement(); + s->GetTreeString(stream, 1); + REQUIRE(stream.str() == "\tBreakStatement"); +} + +TEST_CASE( "Block Statement To String", "[BoundTreeString]" ) { + std::stringstream stream; + auto s = new BoundBlockStatement({new BoundBreakStatement(), new BoundBreakStatement()}); + s->GetTreeString(stream, 1); + REQUIRE(stream.str() == "\tBlockStatement\n\t\tBreakStatement\n\t\tBreakStatement"); +} + +#endif \ No newline at end of file diff --git a/tests/integration/EqualityOperationsTests.cpp b/tests/integration/EqualityOperationsTests.cpp index 94476da..5f0cbd0 100644 --- a/tests/integration/EqualityOperationsTests.cpp +++ b/tests/integration/EqualityOperationsTests.cpp @@ -153,6 +153,23 @@ TEST_CASE( "5 >= 5 == true", "[integration]" ) { delete script; } +TEST_CASE( "nil == nil == true", "[integration]" ) { + auto script = Script::Create("nil == nil"); + REQUIRE(!script->Diagnostics -> HasErrors()); + auto result = script->Evaluate(); + REQUIRE(result->EvaluateBool()); + delete script; +} + +TEST_CASE( "nil != nil == true", "[integration]" ) { + auto script = Script::Create("nil ~= nil"); + REQUIRE(!script->Diagnostics -> HasErrors()); + auto result = script->Evaluate(); + REQUIRE(!result->EvaluateBool()); + delete script; +} + + #endif