From e61bf08db3ef7762587cea7cc226a48f8bc5252c Mon Sep 17 00:00:00 2001 From: Digit16 Date: Mon, 3 Jun 2024 22:16:37 +0200 Subject: [PATCH] Godot integration with interpreter --- interpreter/CMakeLists.txt | 23 --- interpreter/CodeInterpreter.cpp | 13 ++ interpreter/CodeInterpreter.h | 17 ++ interpreter/SCsub | 8 + interpreter/config.py | 7 + interpreter/internal/Intepreter.cpp | 284 ++++++++++++++++++++++++++ interpreter/internal/Interpreter.h | 205 +++++++++++++++++++ interpreter/register_types.cpp | 20 ++ interpreter/register_types.h | 7 + interpreter/src/Ast.h | 106 ---------- interpreter/src/Interpreter.h | 86 -------- interpreter/src/Lexer.h | 91 --------- interpreter/src/Parser.h | 103 ---------- interpreter/src/Token.h | 80 -------- interpreter/test/CMakeLists.txt | 16 -- interpreter/test/InterpreterTests.cpp | 70 ------- interpreter/tests/test_interpreter.h | 18 ++ 17 files changed, 579 insertions(+), 575 deletions(-) delete mode 100644 interpreter/CMakeLists.txt create mode 100644 interpreter/CodeInterpreter.cpp create mode 100644 interpreter/CodeInterpreter.h create mode 100644 interpreter/SCsub create mode 100644 interpreter/config.py create mode 100644 interpreter/internal/Intepreter.cpp create mode 100644 interpreter/internal/Interpreter.h create mode 100644 interpreter/register_types.cpp create mode 100644 interpreter/register_types.h delete mode 100644 interpreter/src/Ast.h delete mode 100644 interpreter/src/Interpreter.h delete mode 100644 interpreter/src/Lexer.h delete mode 100644 interpreter/src/Parser.h delete mode 100644 interpreter/src/Token.h delete mode 100644 interpreter/test/CMakeLists.txt delete mode 100644 interpreter/test/InterpreterTests.cpp create mode 100644 interpreter/tests/test_interpreter.h diff --git a/interpreter/CMakeLists.txt b/interpreter/CMakeLists.txt deleted file mode 100644 index 9ba9988..0000000 --- a/interpreter/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -cmake_minimum_required(VERSION 3.0.0) -project(Interpreter VERSION 0.1.0) - -find_package(spdlog REQUIRED) -find_package(GTest CONFIG REQUIRED) - -set(CPACK_PROJECT_NAME ${PROJECT_NAME}) -set(CPACK_PROJECT_VERSION ${PROJECT_VERSION}) -include(CPack) - -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra -std=c++20") - -set(SOURCES main.cpp src/Ast.h src/Lexer src/Interpreter.h src/Token.h) -add_executable(Interpreter ${SOURCES}) - -target_include_directories(Interpreter PUBLIC ./src) - -target_link_libraries(Interpreter - spdlog::spdlog - spdlog -) - -add_subdirectory(test) diff --git a/interpreter/CodeInterpreter.cpp b/interpreter/CodeInterpreter.cpp new file mode 100644 index 0000000..5806148 --- /dev/null +++ b/interpreter/CodeInterpreter.cpp @@ -0,0 +1,13 @@ +#include "CodeInterpreter.h" + +void CodeInterpreter::_bind_methods() { + ClassDB::bind_method(D_METHOD("interpret"), &CodeInterpreter::interpret); +} + +String CodeInterpreter::interpret(const String& str) { + std::string text{str.utf8().get_data()}; + + std::shared_ptr result = Interpreter::interpret(text); + auto number = std::dynamic_pointer_cast(result); + return String(std::to_string(number->value()).c_str()); +} \ No newline at end of file diff --git a/interpreter/CodeInterpreter.h b/interpreter/CodeInterpreter.h new file mode 100644 index 0000000..c78521d --- /dev/null +++ b/interpreter/CodeInterpreter.h @@ -0,0 +1,17 @@ +#ifndef CodeInterpreter_H +#define CodeInterpreter_H + + +#include "internal/Interpreter.h" +#include "core/object/ref_counted.h" + +class CodeInterpreter : public RefCounted, public Interpreter { + GDCLASS(CodeInterpreter, RefCounted); + +protected: + static void _bind_methods(); +public: + String interpret(const String& str); +}; + +#endif \ No newline at end of file diff --git a/interpreter/SCsub b/interpreter/SCsub new file mode 100644 index 0000000..0368ec5 --- /dev/null +++ b/interpreter/SCsub @@ -0,0 +1,8 @@ +# SCsub + +Import('env') + +env.add_source_files(env.modules_sources, "*.cpp") # Add all cpp files to the build +env.add_source_files(env.modules_sources, "internal/*.cpp") +env.add_source_files(env.modules_sources, "tests/*.cpp") +# env.Append(CPPPATH=["./internal:include"]) \ No newline at end of file diff --git a/interpreter/config.py b/interpreter/config.py new file mode 100644 index 0000000..c1c0259 --- /dev/null +++ b/interpreter/config.py @@ -0,0 +1,7 @@ +# config.py + +def can_build(env, platform): + return True + +def configure(env): + pass \ No newline at end of file diff --git a/interpreter/internal/Intepreter.cpp b/interpreter/internal/Intepreter.cpp new file mode 100644 index 0000000..6086f82 --- /dev/null +++ b/interpreter/internal/Intepreter.cpp @@ -0,0 +1,284 @@ +#include "Interpreter.h" + +// static +std::string Token::typeToString(TokenType type) { + switch(type) { + case TokenType::INTEGER: + return "INTEGER"; + case TokenType::PLUS: + return "PLUS"; + case TokenType::MINUS: + return "MINUS"; + case TokenType::MULTIPLICATION: + return "MULTIPLICATION"; + case TokenType::DIVISION: + return "DIVISION"; + case TokenType::LPAREN: + return "LPAREN"; + case TokenType::RPAREN: + return "RPAREN"; + case TokenType::END_OF_FILE: + return "END_OF_FILE"; + case TokenType::NONE: + return "NONE"; + } + + throw std::runtime_error("TokenType not implemented, string conversion is not possible"); +} + +std::string Token::toString() const { + if(std::holds_alternative(_value)) { + return std::string("TOKEN(" + typeToString(_type) + "," + std::to_string(std::get(_value)) + ")"); + } else if(std::holds_alternative(_value)) { + return std::string("TOKEN(" + typeToString(_type) + "," + std::get(_value) + ")"); + } else if(std::holds_alternative(_value)) { + return std::string("TOKEN(" + typeToString(_type) + ",NONE)"); + } else { + throw std::runtime_error(std::string("Variant holds unsupported type '" + std::to_string(_value.index()) + "'")); + } +} + +int Token::getIntegerValue() const { + if(std::holds_alternative(_value)) { + return std::get(_value); + } + + throw std::runtime_error("Variant does not hold integer inside, cannot return value of it"); +} + +std::variant NodeConverter::getVariant(const std::shared_ptr& node) { + switch (node->nodeType()) + { + case NodeType::NUMBER: { + auto number = std::dynamic_pointer_cast(node); + if (!number) { + throw std::runtime_error("Casting failed"); + } + + return std::variant(Number(*number)); + } + case NodeType::BINARY_OPERATION: { + auto binOp = std::dynamic_pointer_cast(node); + if (!binOp) { + throw std::runtime_error("Casting failed"); + } + + return std::variant(BinaryOperation(*binOp)); + } + } + + throw std::runtime_error("Unknown NodeType"); +} + +void Lexer::advance() { + ++_pos; + if (_pos > static_cast(_text.length()) - 1) { + _currentChar = '\0'; + } else { + _currentChar = _text[_pos]; + } +} + +void Lexer::skipWhitespace() { + while (_currentChar != '\0' && _currentChar == ' ') { + advance(); + } +} + +int Lexer::integer() { + std::string result; + while (_currentChar != '\0' && isdigit(_currentChar)) { + result += _currentChar; + advance(); + } + + return std::stoi(result); +} + +Token Lexer::getNextToken() { + while (_currentChar != '\0') + { + if (_currentChar == ' ') { + skipWhitespace(); + continue; + } + if (isdigit(_currentChar)) { + return Token(integer(), TokenType::INTEGER); + } + if (_currentChar == '+') { + advance(); + return Token('+', TokenType::PLUS); + } + if (_currentChar == '-') { + advance(); + return Token('-', TokenType::MINUS); + } + if (_currentChar == '*') { + advance(); + return Token('*', TokenType::MULTIPLICATION); + } + if (_currentChar == '/') { + advance(); + return Token('/', TokenType::DIVISION); + } + if (_currentChar == '(') { + advance(); + return Token('(', TokenType::LPAREN); + } + if (_currentChar == ')') { + advance(); + return Token(')', TokenType::RPAREN); + } + + raiseInvalidCharacterError(); + } + + return Token(std::nullptr_t(), TokenType::END_OF_FILE); +} + +void Parser::eat(TokenType tokenType) { + if (_currentToken.getType() == tokenType) { + _currentToken = _lexer.getNextToken(); + } else { + raiseInvalidSyntaxError(); + } +} + +// factor : INTEGER | LPAREN expr RPAREN +std::shared_ptr Parser::factor() { + Token token = _currentToken; + if (token.getType() == TokenType::INTEGER) { + eat(TokenType::INTEGER); + + return std::make_shared(token); + } else if (token.getType() == TokenType::LPAREN) { + eat(TokenType::LPAREN); + std::shared_ptr result = expr(); + eat(TokenType::RPAREN); + + auto binOp = std::dynamic_pointer_cast(result); + if (!binOp) { + throw std::runtime_error("Casting failed"); + } + + return std::make_shared(*binOp); + } else { + throw std::runtime_error("Unknown token type in factor method"); + } +} + +// term : factor ((MUL | DIV) factor)* +std::shared_ptr Parser::term() { + std::shared_ptr node = factor(); + while ((_currentToken.getType() == TokenType::MULTIPLICATION) || + (_currentToken.getType() == TokenType::DIVISION)) { + const Token token = _currentToken; + + if (token.getType() == TokenType::MULTIPLICATION) { + eat(TokenType::MULTIPLICATION); + } else if (token.getType() == TokenType::DIVISION) { + eat(TokenType::DIVISION); + } + + const auto left = node; + const auto right = factor(); + node = std::make_shared(left, token, right); + } + + return node; +} + +/* +expr : term ((PLUS | MINUS) term)* +term : factor ((MUL | DIV) factor)* +factor : INTEGER +*/ +std::shared_ptr Parser::expr() { + std::shared_ptr node = term(); + while ((_currentToken.getType() == TokenType::PLUS) || + (_currentToken.getType() == TokenType::MINUS)) { + const Token token = _currentToken; + + if (token.getType() == TokenType::PLUS) { + eat(TokenType::PLUS); + } else if (token.getType() == TokenType::MINUS) { + eat(TokenType::MINUS); + } + + const auto left = node; + const auto right = term(); + node = std::make_shared(left, token, right); + } + + return node; +} + +using Visitor = NodeVisitor::VisitNode; + +// std::shared_ptr Visitor::operator()(auto& node) { throw std::runtime_error("Unsupported AstNode in NodeVisitor"); } + +std::shared_ptr Visitor::operator()(BinaryOperation& node) { + + std::variant left = NodeConverter::getVariant(node.left()); + std::variant right = NodeConverter::getVariant(node.right()); + + TokenType tokenType = node.binaryOperator().getType(); + if (tokenType == TokenType::PLUS) { + int value = calculateResult(visit(left), visit(right), tokenType); + return std::make_shared(Token(value, TokenType::INTEGER)); + } else if (tokenType == TokenType::MINUS) { + int value = calculateResult(visit(left), visit(right), tokenType); + return std::make_shared(Token(value, TokenType::INTEGER)); + } else if (tokenType == TokenType::MULTIPLICATION) { + int value = calculateResult(visit(left), visit(right), tokenType); + return std::make_shared(Token(value, TokenType::INTEGER)); + } else if (tokenType == TokenType::DIVISION) { + int value = calculateResult(visit(left), visit(right), tokenType); + return std::make_shared(Token(value, TokenType::INTEGER)); + } else { + throw std::runtime_error("Unknown type of binary operator in visitor"); + } +} + +std::shared_ptr Visitor::operator()(Number& node) { + return std::make_shared(Token(node.value(), TokenType::INTEGER)); +} + +std::shared_ptr Interpreter::interpret(const std::string& text) { + Lexer lexer{text}; + Parser parser{lexer}; + + std::shared_ptr tree = parser.parse(); + return visit(NodeConverter::getVariant(tree)); +} + +// static +std::shared_ptr NodeVisitor::visit(std::variant astNode) { + return std::visit(VisitNode(), astNode); +} + +// static +int NodeVisitor::calculateResult(const std::shared_ptr& left, const std::shared_ptr& right, TokenType type) +{ + auto leftNumber = std::dynamic_pointer_cast(left); + auto rightNumber = std::dynamic_pointer_cast(right); + + if ((!leftNumber) || (!rightNumber)) { + throw std::runtime_error("Casting failed"); + } + + int leftValue = leftNumber->value(); + int rightValue = rightNumber->value(); + + if(type == TokenType::PLUS) { + return leftValue + rightValue; + } else if(type == TokenType::MINUS) { + return leftValue - rightValue; + } else if(type == TokenType::MULTIPLICATION) { + return leftValue * rightValue; + } else if(type == TokenType::DIVISION){ + return leftValue / rightValue; + } else { + throw std::runtime_error("Unknown token type in calculateResult"); + } +} diff --git a/interpreter/internal/Interpreter.h b/interpreter/internal/Interpreter.h new file mode 100644 index 0000000..a63f933 --- /dev/null +++ b/interpreter/internal/Interpreter.h @@ -0,0 +1,205 @@ +#pragma once + +#include "core/string/ustring.h" + +#include +#include +#include +#include +#include +#include +#include + + +enum class TokenType : uint8_t { + INTEGER = 1, + PLUS = 2, + MINUS = 3, + MULTIPLICATION = 4, + DIVISION = 5, + LPAREN = 6, + RPAREN = 7, + END_OF_FILE = 8, + NONE = 9 +}; + +class Token { +public: + explicit Token(std::variant value, TokenType type) : _value(value), _type(type) {}; + + static std::string typeToString(TokenType type); + + std::string toString() const; + + int getIntegerValue() const; + + TokenType getType() const { return _type; } + +private: + std::variant _value; + TokenType _type; +}; + +class BinaryOperation; +class Number; + +enum class NodeType : uint8_t { + NUMBER = 0, + BINARY_OPERATION = 1 +}; + +class AstNode { +public: + AstNode() = default; + AstNode(const AstNode&) = default; + AstNode(AstNode&&) = default; + AstNode& operator=(const AstNode&) = default; + AstNode& operator=(AstNode&&) = default; + virtual ~AstNode() = default; + + virtual void print() const = 0; + virtual std::string toString() const = 0; + + virtual Token token() const = 0; + virtual int value() const = 0; + + virtual NodeType nodeType() const = 0; +}; + +class Number : public AstNode { +public: + Number(const Token& token) : _token(token) {} + + void print() const override { + std::cout << std::to_string(_token.getIntegerValue()) << std::endl; + } + + virtual std::string toString() const override { + return std::to_string(_token.getIntegerValue()); + } + + Token token() const override { + return _token; + } + int value() const override { + return _token.getIntegerValue(); + } + + NodeType nodeType() const override { + return NodeType::NUMBER; + } + +private: + Token _token; +}; + +class BinaryOperation : public AstNode { +public: + BinaryOperation(const std::shared_ptr& left, const Token& op, const std::shared_ptr& right) : + _left(left), + _operator(op), + _right(right) {} + + void print() const override { + std::cout << "(" << _left->toString() << " " << _operator.toString() << " " << _right->toString() << ")" << std::endl; + } + std::string toString() const override { + return std::string("(" + _left->toString() + " " + _operator.toString() + " " + _right->toString() + ")"); + } + + Token token() const override { + throw std::runtime_error("Cannot return token from BinaryOperation node"); + } + int value() const override { + throw std::runtime_error("Cannot return value from BinaryOperation node"); + } + + std::shared_ptr left() const { + return _left; + } + Token binaryOperator() const { + return _operator; + } + std::shared_ptr right() const { + return _right; + } + + NodeType nodeType() const override { + return NodeType::BINARY_OPERATION; + } + +private: + std::shared_ptr _left; + Token _operator; + std::shared_ptr _right; +}; + +class NodeConverter { +public: + static std::variant getVariant(const std::shared_ptr& node); +}; + +class Lexer { +public: + Lexer(const std::string& text) : _text(text), _pos(0), _currentChar(_text.at(_pos)) {} + + void raiseInvalidCharacterError() const { throw std::runtime_error("Invalid character"); } + + void advance(); + + void skipWhitespace(); + + int integer(); + + Token getNextToken(); + +private: + std::string _text; + int _pos; + char _currentChar; +}; + +class Parser { +public: + explicit Parser(const Lexer& lexer) : _lexer(lexer), _currentToken(_lexer.getNextToken()) {} + + void raiseInvalidSyntaxError() const { throw std::runtime_error("Invalid syntax"); } + + void eat(TokenType tokenType); + + std::shared_ptr factor(); + + std::shared_ptr term(); + + std::shared_ptr expr(); + + std::shared_ptr parse() { return expr(); } + +private: + Lexer _lexer; + Token _currentToken; +}; + +class NodeVisitor { +public: + struct VisitNode + { + // std::shared_ptr operator()(auto& node); + std::shared_ptr operator()(BinaryOperation& node); + std::shared_ptr operator()(Number& node); + }; + + static std::shared_ptr visit(std::variant astNode); + + static int calculateResult(const std::shared_ptr& left, const std::shared_ptr& right, TokenType type); +}; + +class Interpreter : public NodeVisitor +{ +public: + Interpreter() {} + // Interpreter(const std::string& text) : _lexer(text), _parser(_lexer) {} + + std::shared_ptr interpret(const std::string& text); +}; + diff --git a/interpreter/register_types.cpp b/interpreter/register_types.cpp new file mode 100644 index 0000000..ea3eead --- /dev/null +++ b/interpreter/register_types.cpp @@ -0,0 +1,20 @@ +/* register_types.cpp */ + +#include "register_types.h" + +#include "core/object/class_db.h" +#include "CodeInterpreter.h" + +void initialize_interpreter_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + GDREGISTER_CLASS(CodeInterpreter); +} + +void uninitialize_interpreter_module(ModuleInitializationLevel p_level) { + if (p_level != MODULE_INITIALIZATION_LEVEL_SCENE) { + return; + } + // Nothing to do here in this example. +} \ No newline at end of file diff --git a/interpreter/register_types.h b/interpreter/register_types.h new file mode 100644 index 0000000..cff1cdc --- /dev/null +++ b/interpreter/register_types.h @@ -0,0 +1,7 @@ +/* register_types.h */ + +#include "modules/register_module_types.h" + +void initialize_interpreter_module(ModuleInitializationLevel p_level); +void uninitialize_interpreter_module(ModuleInitializationLevel p_level); +/* yes, the word in the middle must be the same as the module folder name */ \ No newline at end of file diff --git a/interpreter/src/Ast.h b/interpreter/src/Ast.h deleted file mode 100644 index 180f43c..0000000 --- a/interpreter/src/Ast.h +++ /dev/null @@ -1,106 +0,0 @@ -#pragma once - -#include "Token.h" -#include - -class BinaryOperation; -class Number; - -enum class NodeType : uint8_t { NUMBER = 0, BINARY_OPERATION = 1 }; - -class AstNode { -public: - AstNode() = default; - AstNode(const AstNode &) = default; - AstNode(AstNode &&) = default; - AstNode &operator=(const AstNode &) = default; - AstNode &operator=(AstNode &&) = default; - virtual ~AstNode() = default; - - virtual void print() const = 0; - virtual std::string toString() const = 0; - - virtual Token token() const = 0; - virtual int value() const = 0; - - virtual NodeType nodeType() const = 0; -}; - -class Number : public AstNode { -public: - Number(const Token &token) : _token(token) {} - - void print() const override { - SPDLOG_INFO(std::to_string(_token.getIntegerValue())); - } - - virtual std::string toString() const override { - return std::to_string(_token.getIntegerValue()); - } - - Token token() const override { return _token; } - int value() const override { return _token.getIntegerValue(); } - - NodeType nodeType() const override { return NodeType::NUMBER; } - -private: - Token _token; -}; - -class BinaryOperation : public AstNode { -public: - BinaryOperation(const std::shared_ptr &left, const Token &op, - const std::shared_ptr &right) - : _left(left), _operator(op), _right(right) {} - - void print() const override { - SPDLOG_INFO("({} {} {})", _left->toString(), _operator.toString(), - _right->toString()); - } - std::string toString() const override { - return fmt::format("({} {} {})", _left->toString(), _operator.toString(), - _right->toString()); - } - - Token token() const override { - throw std::runtime_error("Cannot return token from BinaryOperation node"); - } - int value() const override { - throw std::runtime_error("Cannot return value from BinaryOperation node"); - } - - std::shared_ptr left() const { return _left; } - Token binaryOperator() const { return _operator; } - std::shared_ptr right() const { return _right; } - - NodeType nodeType() const override { return NodeType::BINARY_OPERATION; } - -private: - std::shared_ptr _left; - Token _operator; - std::shared_ptr _right; -}; - -std::variant -getVariant(const std::shared_ptr &node) { - switch (node->nodeType()) { - case NodeType::NUMBER: { - auto number = std::dynamic_pointer_cast(node); - if (!number) { - throw std::runtime_error("Casting failed"); - } - - return std::variant(Number(*number)); - } - case NodeType::BINARY_OPERATION: { - auto binOp = std::dynamic_pointer_cast(node); - if (!binOp) { - throw std::runtime_error("Casting failed"); - } - - return std::variant(BinaryOperation(*binOp)); - } - } - - throw std::runtime_error("Unknown NodeType"); -} diff --git a/interpreter/src/Interpreter.h b/interpreter/src/Interpreter.h deleted file mode 100644 index d1c3653..0000000 --- a/interpreter/src/Interpreter.h +++ /dev/null @@ -1,86 +0,0 @@ -#pragma once - -#include "Ast.h" -#include "Lexer.h" -#include "Parser.h" -#include "Token.h" - -#include - -class NodeVisitor { -public: - struct VisitNode { - std::shared_ptr operator()(auto &node) { - throw std::runtime_error("Unsupported AstNode in NodeVisitor"); - } - std::shared_ptr operator()(BinaryOperation &node) { - - std::variant left = getVariant(node.left()); - std::variant right = getVariant(node.right()); - - TokenType tokenType = node.binaryOperator().getType(); - if (tokenType == TokenType::PLUS) { - int value = calculateResult(visit(left), visit(right), tokenType); - return std::make_shared(Token(value, TokenType::INTEGER)); - } else if (tokenType == TokenType::MINUS) { - int value = calculateResult(visit(left), visit(right), tokenType); - return std::make_shared(Token(value, TokenType::INTEGER)); - } else if (tokenType == TokenType::MULTIPLICATION) { - int value = calculateResult(visit(left), visit(right), tokenType); - return std::make_shared(Token(value, TokenType::INTEGER)); - } else if (tokenType == TokenType::DIVISION) { - int value = calculateResult(visit(left), visit(right), tokenType); - return std::make_shared(Token(value, TokenType::INTEGER)); - } else { - throw std::runtime_error("Unknown type of binary operator in visitor"); - } - } - std::shared_ptr operator()(Number &node) { - return std::make_shared(Token(node.value(), TokenType::INTEGER)); - } - }; - - static std::shared_ptr - visit(std::variant astNode) { - return std::visit(VisitNode(), astNode); - } - - static int calculateResult(const std::shared_ptr &left, - const std::shared_ptr &right, - TokenType type) { - auto leftNumber = std::dynamic_pointer_cast(left); - auto rightNumber = std::dynamic_pointer_cast(right); - - if ((!leftNumber) || (!rightNumber)) { - throw std::runtime_error("Casting failed"); - } - - int leftValue = leftNumber->value(); - int rightValue = rightNumber->value(); - - if (type == TokenType::PLUS) { - return leftValue + rightValue; - } else if (type == TokenType::MINUS) { - return leftValue - rightValue; - } else if (type == TokenType::MULTIPLICATION) { - return leftValue * rightValue; - } else if (type == TokenType::DIVISION) { - return leftValue / rightValue; - } else { - throw std::runtime_error("Unknown token type in calculateResult"); - } - } -}; - -class Interpreter : public NodeVisitor { -public: - Interpreter(const Parser &parser) : _parser(parser) {} - - std::shared_ptr interpret() { - std::shared_ptr tree = _parser.parse(); - return visit(getVariant(tree)); - } - -private: - Parser _parser; -}; diff --git a/interpreter/src/Lexer.h b/interpreter/src/Lexer.h deleted file mode 100644 index 92618c1..0000000 --- a/interpreter/src/Lexer.h +++ /dev/null @@ -1,91 +0,0 @@ -#pragma once - -#include "Token.h" - -class Lexer { -public: - Lexer(const std::string &text) - : _text(text), _pos(0), _currentChar(_text.at(_pos)) {} - - void raiseInvalidCharacterError() const { - throw std::runtime_error("Invalid character"); - } - - void advance() { - ++_pos; - if (_pos > static_cast(_text.length()) - 1) { - _currentChar = '\0'; - } else { - _currentChar = _text[_pos]; - } - } - - void skipWhitespace() { - while (_currentChar != '\0' && _currentChar == ' ') { - advance(); - } - } - - int integer() { - std::string result; - while (_currentChar != '\0' && isdigit(_currentChar)) { - result += _currentChar; - advance(); - } - - return std::stoi(result); - } - - Token getNextToken() { - while (_currentChar != '\0') { - - if (_currentChar == ' ') { - skipWhitespace(); - continue; - } - - if (isdigit(_currentChar)) { - return Token(integer(), TokenType::INTEGER); - } - - if (_currentChar == '+') { - advance(); - return Token('+', TokenType::PLUS); - } - - if (_currentChar == '-') { - advance(); - return Token('-', TokenType::MINUS); - } - - if (_currentChar == '*') { - advance(); - return Token('*', TokenType::MULTIPLICATION); - } - - if (_currentChar == '/') { - advance(); - return Token('/', TokenType::DIVISION); - } - - if (_currentChar == '(') { - advance(); - return Token('(', TokenType::LPAREN); - } - - if (_currentChar == ')') { - advance(); - return Token(')', TokenType::RPAREN); - } - - raiseInvalidCharacterError(); - } - - return Token(std::nullptr_t(), TokenType::END_OF_FILE); - } - -private: - std::string _text; - int _pos; - char _currentChar; -}; diff --git a/interpreter/src/Parser.h b/interpreter/src/Parser.h deleted file mode 100644 index 6fe7cd6..0000000 --- a/interpreter/src/Parser.h +++ /dev/null @@ -1,103 +0,0 @@ -#pragma once - -#include -#include -#include - -#include "Ast.h" -#include "Lexer.h" -#include "Token.h" - -class Parser { -public: - explicit Parser(const Lexer &lexer) - : _lexer(lexer), _currentToken(_lexer.getNextToken()) {} - - void raiseInvalidSyntaxError() const { - throw std::runtime_error("Invalid syntax"); - } - - void eat(TokenType tokenType) { - if (_currentToken.getType() == tokenType) { - _currentToken = _lexer.getNextToken(); - } else { - raiseInvalidSyntaxError(); - } - } - - // factor : INTEGER | LPAREN expr RPAREN - std::shared_ptr factor() { - Token token = _currentToken; - if (token.getType() == TokenType::INTEGER) { - eat(TokenType::INTEGER); - - return std::make_shared(token); - } else if (token.getType() == TokenType::LPAREN) { - eat(TokenType::LPAREN); - std::shared_ptr result = expr(); - eat(TokenType::RPAREN); - - auto binOp = std::dynamic_pointer_cast(result); - if (!binOp) { - throw std::runtime_error("Casting failed"); - } - return std::make_shared(*binOp); - } else { - throw std::runtime_error("Unknown token type in factor method"); - } - } - - // term : factor ((MUL | DIV) factor)* - std::shared_ptr term() { - std::shared_ptr node = factor(); - - while ((_currentToken.getType() == TokenType::MULTIPLICATION) || - (_currentToken.getType() == TokenType::DIVISION)) { - - const Token token = _currentToken; - if (token.getType() == TokenType::MULTIPLICATION) { - eat(TokenType::MULTIPLICATION); - } else if (token.getType() == TokenType::DIVISION) { - eat(TokenType::DIVISION); - } - - const auto left = node; - const auto right = factor(); - node = std::make_shared(left, token, right); - } - - return node; - } - - /* - expr : term ((PLUS | MINUS) term)* - term : factor ((MUL | DIV) factor)* - factor : INTEGER - */ - std::shared_ptr expr() { - std::shared_ptr node = term(); - - while ((_currentToken.getType() == TokenType::PLUS) || - (_currentToken.getType() == TokenType::MINUS)) { - - const Token token = _currentToken; - if (token.getType() == TokenType::PLUS) { - eat(TokenType::PLUS); - } else if (token.getType() == TokenType::MINUS) { - eat(TokenType::MINUS); - } - - const auto left = node; - const auto right = term(); - node = std::make_shared(left, token, right); - } - - return node; - } - - std::shared_ptr parse() { return expr(); } - -private: - Lexer _lexer; - Token _currentToken; -}; diff --git a/interpreter/src/Token.h b/interpreter/src/Token.h deleted file mode 100644 index b3f0e5b..0000000 --- a/interpreter/src/Token.h +++ /dev/null @@ -1,80 +0,0 @@ -#pragma once - -#include -#include -#include -#include - -enum class TokenType : uint8_t { - INTEGER = 1, - PLUS = 2, - MINUS = 3, - MULTIPLICATION = 4, - DIVISION = 5, - LPAREN = 6, - RPAREN = 7, - END_OF_FILE = 8, - NONE = 9 -}; - -class Token { -public: - explicit Token(std::variant value, TokenType type) - : _value(value), _type(type){}; - - static std::string typeToString(TokenType type) { - switch (type) { - case TokenType::INTEGER: - return "INTEGER"; - case TokenType::PLUS: - return "PLUS"; - case TokenType::MINUS: - return "MINUS"; - case TokenType::MULTIPLICATION: - return "MULTIPLICATION"; - case TokenType::DIVISION: - return "DIVISION"; - case TokenType::LPAREN: - return "LPAREN"; - case TokenType::RPAREN: - return "RPAREN"; - case TokenType::END_OF_FILE: - return "END_OF_FILE"; - case TokenType::NONE: - return "NONE"; - } - - throw std::runtime_error( - "TokenType not implemented, string conversion is not possible"); - } - - std::string toString() const { - if (std::holds_alternative(_value)) { - return std::format("TOKEN({},{})", typeToString(_type), - std::to_string(std::get(_value))); - } else if (std::holds_alternative(_value)) { - return std::format("TOKEN({},{})", typeToString(_type), - std::get(_value)); - } else if (std::holds_alternative(_value)) { - return std::format("TOKEN({},{})", typeToString(_type), "NONE"); - } else { - throw std::runtime_error( - std::format("Variant holds unsupported type '{}", _value.index())); - } - } - - TokenType getType() const { return _type; } - - int getIntegerValue() const { - if (std::holds_alternative(_value)) { - return std::get(_value); - } - - throw std::runtime_error( - "Variant does not hold integer inside, cannot return value of it"); - } - -private: - std::variant _value; - TokenType _type; -}; diff --git a/interpreter/test/CMakeLists.txt b/interpreter/test/CMakeLists.txt deleted file mode 100644 index fab6e84..0000000 --- a/interpreter/test/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -enable_testing() - -add_executable(InterpreterTests - InterpreterTests.cpp -) - -target_include_directories(InterpreterTests PUBLIC ../src) - -target_link_libraries(InterpreterTests - GTest::gtest_main - spdlog::spdlog - spdlog -) - -include(GoogleTest) -gtest_discover_tests(InterpreterTests) diff --git a/interpreter/test/InterpreterTests.cpp b/interpreter/test/InterpreterTests.cpp deleted file mode 100644 index 8ddd241..0000000 --- a/interpreter/test/InterpreterTests.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include - -#include "../src/Ast.h" -#include "../src/Interpreter.h" -#include "../src/Lexer.h" -#include "../src/Token.h" - -using namespace testing; - -class InterpreterTests : public Test { -public: - void SetUp() override {} -}; - -TEST_F(InterpreterTests, SimpleAdditionTest) { - std::string input = "5+7"; - - Lexer lexer(input); - Parser parser(lexer); - Interpreter interpreter(parser); - std::shared_ptr result = interpreter.interpret(); - - int numberResult = -1; - auto number = std::dynamic_pointer_cast(result); - if (!number) { - throw std::runtime_error("Casting failed"); - } - - numberResult = number->value(); - - ASSERT_EQ(numberResult, 12); -} - -TEST_F(InterpreterTests, SimpleNestedExpressionTest) { - std::string input = "(5+7)*2"; - - Lexer lexer(input); - Parser parser(lexer); - Interpreter interpreter(parser); - std::shared_ptr result = interpreter.interpret(); - - int numberResult = -1; - auto number = std::dynamic_pointer_cast(result); - if (!number) { - throw std::runtime_error("Casting failed"); - } - - numberResult = number->value(); - - ASSERT_EQ(numberResult, 24); -} - -TEST_F(InterpreterTests, ProperExecutionOrder) { - std::string input = "5+7*5"; - - Lexer lexer(input); - Parser parser(lexer); - Interpreter interpreter(parser); - std::shared_ptr result = interpreter.interpret(); - - int numberResult = -1; - auto number = std::dynamic_pointer_cast(result); - if (!number) { - throw std::runtime_error("Casting failed"); - } - - numberResult = number->value(); - - ASSERT_EQ(numberResult, 40); -} diff --git a/interpreter/tests/test_interpreter.h b/interpreter/tests/test_interpreter.h new file mode 100644 index 0000000..571625c --- /dev/null +++ b/interpreter/tests/test_interpreter.h @@ -0,0 +1,18 @@ +#ifndef TEST_INTERPRETER_H +#define TEST_INTERPRETER_H + +#include "tests/test_macros.h" + +#include "../CodeInterpreter.h" + +namespace TestInterpreter { + +TEST_CASE("[Interpreter] 2+2") { + CodeInterpreter interpreter{}; + String rv = interpreter.interpret("2+2"); + CHECK(rv == "4"); +} + +} + +#endif // TEST_INTERPRETER_H \ No newline at end of file