A simple scripting language in C++
Ferenc Szontágh
2025-04-15 3895272a7f238c9aef0b584bd3b10b900445245d
refactored
2 files modified
8 files renamed
17 files added
12 files deleted
3011 ■■■■ changed files
CMakeLists.txt 6 ●●●● patch | view | raw | blame | history
cli/main.cpp 25 ●●●● patch | view | raw | blame | history
include/FunctionSymbol.hpp 31 ●●●●● patch | view | raw | blame | history
include/VariableSymbol.hpp 20 ●●●●● patch | view | raw | blame | history
include/VariableTypes.hpp 46 ●●●●● patch | view | raw | blame | history
src/Builtins/BaseFunction.hpp patch | view | raw | blame | history
src/Interpreter/ExpressionNode.hpp 17 ●●●●● patch | view | raw | blame | history
src/Interpreter/Interpreter.hpp 49 ●●●●● patch | view | raw | blame | history
src/Interpreter/Operation.hpp 31 ●●●●● patch | view | raw | blame | history
src/Interpreter/OperationContainer.hpp 12 ●●●●● patch | view | raw | blame | history
src/Lexer.cpp 352 ●●●●● patch | view | raw | blame | history
src/Lexer.hpp 77 ●●●●● patch | view | raw | blame | history
src/Lexer/Lexer.cpp 14 ●●●●● patch | view | raw | blame | history
src/Lexer/Lexer.hpp 316 ●●●●● patch | view | raw | blame | history
src/Lexer/Token.hpp 37 ●●●●● patch | view | raw | blame | history
src/Lexer/TokenType.hpp 87 ●●●●● patch | view | raw | blame | history
src/Parser/Parser.cpp 37 ●●●●● patch | view | raw | blame | history
src/Parser/Parser.hpp 536 ●●●●● patch | view | raw | blame | history
src/ScriptException.hpp 148 ●●●●● patch | view | raw | blame | history
src/ScriptExceptionMacros.h 55 ●●●●● patch | view | raw | blame | history
src/ScriptInterpreter.cpp 379 ●●●●● patch | view | raw | blame | history
src/ScriptInterpreter.hpp 140 ●●●●● patch | view | raw | blame | history
src/ScriptInterpreterHelpers.hpp 98 ●●●●● patch | view | raw | blame | history
src/StringHelpers.hpp 20 ●●●●● patch | view | raw | blame | history
src/Symbols/BaseSymbol.hpp 4 ●●● patch | view | raw | blame | history
src/Symbols/ConstantSymbol.hpp patch | view | raw | blame | history
src/Symbols/FunctionSymbol.hpp 41 ●●●●● patch | view | raw | blame | history
src/Symbols/ParameterContainer.hpp 17 ●●●●● patch | view | raw | blame | history
src/Symbols/SymbolContainer.hpp patch | view | raw | blame | history
src/Symbols/SymbolFactory.hpp 24 ●●●● patch | view | raw | blame | history
src/Symbols/SymbolKind.hpp patch | view | raw | blame | history
src/Symbols/SymbolTable.hpp 1 ●●●● patch | view | raw | blame | history
src/Symbols/SymbolTypes.hpp patch | view | raw | blame | history
src/Symbols/Value.hpp 77 ●●●●● patch | view | raw | blame | history
src/Symbols/ValueContainer.hpp 12 ●●●●● patch | view | raw | blame | history
src/Symbols/VariableSymbol.hpp 45 ●●●●● patch | view | raw | blame | history
src/Symbols/VariableTypes.hpp 48 ●●●●● patch | view | raw | blame | history
src/Token.hpp 120 ●●●●● patch | view | raw | blame | history
src/VoidScript.hpp 89 ●●●●● patch | view | raw | blame | history
CMakeLists.txt
@@ -113,13 +113,13 @@
configure_file("test_scripts/test1.vs" "test_scripts/test1.vs" @ONLY)
configure_file("test_scripts/test2.vs" "test_scripts/test2.vs" @ONLY)
include_directories(${CMAKE_BINARY_DIR}/include ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include)
include_directories(${CMAKE_BINARY_DIR}/include ${CMAKE_SOURCE_DIR}/src)
# LIBRARY TARGET
add_library(voidscript
            src/ScriptInterpreter.cpp
            src/Lexer.cpp
            src/Parser/Parser.cpp
            src/Lexer/Lexer.cpp
)
install(TARGETS voidscript DESTINATION ${CMAKE_INSTALL_LIBDIR} COMPONENT "lib")
cli/main.cpp
@@ -1,9 +1,10 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <unordered_map>
#include "Builtins/PrintModule.hpp"
#include "Builtins/SleepModule.hpp"
#include "ScriptInterpreter.hpp"
#include "options.h"
#include "VoidScript.hpp"
const std::unordered_map<std::string, std::string> params = {
    { "--help",    "Print this help message"          },
@@ -55,22 +56,8 @@
    const std::string filename = std::filesystem::canonical(file).string();
    try {
        std::ifstream input(filename);
        if (!input.is_open()) {
            std::cerr << "Error: Could not open file " << filename << "\n";
            return 1;
        }
        std::string       content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
        ScriptInterpreter interp;
        interp.registerModule("print", std::make_shared<PrintFunction>());
        interp.registerModule("sleep", std::make_shared<SleepFunction>());
        interp.executeScript(content, filename, "DEFAULT", false);
    } catch (const std::exception & e) {
        std::cerr << "Parser error: " << e.what() << "\n";
        return 1;
    }
    VoidScript voidscript(filename);
    return voidscript.run();
    return 0;
}
include/FunctionSymbol.hpp
File was deleted
include/VariableSymbol.hpp
File was deleted
include/VariableTypes.hpp
File was deleted
src/Builtins/BaseFunction.hpp
src/Interpreter/ExpressionNode.hpp
New file
@@ -0,0 +1,17 @@
#ifndef INTERPRETER_FUNCTION_EXECUTOR_HPP
#define INTERPRETER_FUNCTION_EXECUTOR_HPP
namespace Interpreter {
struct StatementNode {
    virtual ~StatementNode()                                      = default;
    virtual void interpret(class Interpreter & interpreter) const = 0;
};
// Kifejezés (csak int literál most)
struct ExpressionNode {
    virtual ~ExpressionNode()                                   = default;
    virtual int evaluate(class Interpreter & interpreter) const = 0;
};
}  // namespace Interpreter
#endif  // INTERPRETER_FUNCTION_EXECUTOR_HPP
src/Interpreter/Interpreter.hpp
New file
@@ -0,0 +1,49 @@
#ifndef INTERPRETER_HPP
#define INTERPRETER_HPP
#include <memory>
#include <utility>
#include <vector>
#include "Interpreter/Operation.hpp"
#include "Interpreter/OperationContainer.hpp"
#include "Symbols/SymbolContainer.hpp"
namespace Interpreter {
class Interpreter {
  private:
    std::shared_ptr<Symbols::SymbolContainer> symbol_container_;
    const OperationContainer &                operations_conatiner_;
    void setVariable(const std::string & name, int value) {
        //symbol_container_->setVariable(name, value);
    }
  public:
    // Constructor takes the populated symbol container from the parser
    Interpreter(std::shared_ptr<Symbols::SymbolContainer> symbols, const OperationContainer & operations) :
        symbol_container_(std::move(symbols)),
        operations_conatiner_(operations) {}
    void run(const std::vector<Operation> & ops) {
        for (const auto & op : ops) {
            switch (op.type) {
                case OperationType::Assignment:
                    {
                        int value = op.expression->evaluate(*this);
                        setVariable(op.targetVariable, value);
                        break;
                    }
                case OperationType::Expression:
                    {
                        op.expression->evaluate(*this);  // csak side effect miatt
                        break;
                    }
            }
        }
    }
};  // class Interpreter
}  // namespace Interpreter
#endif  // INTERPRETER_HPP
src/Interpreter/Operation.hpp
New file
@@ -0,0 +1,31 @@
#ifndef INTERPRETER_OPERATION_HPP
#define INTERPRETER_OPERATION_HPP
#include <cstdint>
#include <memory>
#include <string>
#include "ExpressionNode.hpp"
namespace Interpreter {
enum OperationType : std::uint8_t {
    Assignment,
    Expression,
};
struct Operation {
    OperationType type;
    // Általános mezők
    std::string targetVariable;
    std::unique_ptr<ExpressionNode> expression;
    Operation(OperationType t, std::string var, std::unique_ptr<ExpressionNode> expr)
        : type(t), targetVariable(std::move(var)), expression(std::move(expr)) {}
};
};  // namespace Interpreter
#endif
src/Interpreter/OperationContainer.hpp
New file
@@ -0,0 +1,12 @@
#ifndef INTERPRETER_OPERATION_CONTAINER_HPP
#define INTERPRETER_OPERATION_CONTAINER_HPP
#include <vector>
#include "Interpreter/Operation.hpp"
namespace Interpreter {
using OperationContainer = std::vector<Operation>;
};  // namespace Interpreter
#endif  // INTERPRETER_OPERATION_CONTAINER_HPP
src/Lexer.cpp
File was deleted
src/Lexer.hpp
File was deleted
src/Lexer/Lexer.cpp
New file
@@ -0,0 +1,14 @@
#include "Lexer/Lexer.hpp"
namespace Lexer {
const std::vector<std::string> Lexer::Lexer::OPERATOR_RELATIONAL = { "==", "!=", "<", ">", "<=", ">=" };
const std::vector<std::string> Lexer::Lexer::OPERATOR_INCREMENT  = { "++", "--" };
const std::vector<std::string> Lexer::Lexer::OPERATOR_ASSIGNMENT = { "=", "+=", "-=", "*=", "/=", "%=" };
const std::vector<std::string> Lexer::Lexer::OPERATOR_LOGICAL    = { "&&", "||" };
const std::vector<std::string> Lexer::Lexer::OPERATOR_ARITHMETIC = { "+", "-", "*", "/", "%" };
const std::vector<std::string> Lexer::Lexer::PUNCTUATION         = { "(", ")", "{", "}", "[", "]", ",", ";" };
};  // namespace Lexer
src/Lexer/Lexer.hpp
New file
@@ -0,0 +1,316 @@
#ifndef LEXER_HPP
#define LEXER_HPP
#include <algorithm>    // std::find_if
#include <cctype>       // <<< Hozzáadva
#include <string>
#include <string_view>  // <<< Hozzáadva
#include <unordered_map>
#include <vector>
#include "Token.hpp"  // Feltételezzük, hogy ez a fenti Token.hpp
namespace Lexer {
class Lexer {
  public:
    Lexer() {
        for (const auto & vecRef :
             { std::cref(OPERATOR_ARITHMETIC), std::cref(OPERATOR_RELATIONAL), std::cref(OPERATOR_INCREMENT),
               std::cref(OPERATOR_ASSIGNMENT), std::cref(OPERATOR_LOGICAL), std::cref(PUNCTUATION) }) {
            for (const auto & str : vecRef.get()) {
                operators_ += str;
            }
        }
        operators_ += "$";
    }
    void addNamespaceInput(const std::string & ns, const std::string & input) {
        inputs_[ns]         = input;
        positions_[ns]      = 0;
        line_numbers_[ns]   = 1;
        column_numbers_[ns] = 1;
    }
    void setNamespace(const std::string & ns) { current_namespace_ = ns; }
    std::vector<Tokens::Token> tokenizeNamespace(const std::string & ns) {
        if (inputs_.find(ns) == inputs_.end()) {
            return {};
        }
        setNamespace(ns);
        std::vector<Tokens::Token> tokens;
        Tokens::Token              token;
        do {
            token = nextToken();
            tokens.push_back(token);
        } while (token.type != Tokens::Type::END_OF_FILE);
        tokens_[ns] = tokens;
        return tokens;
    }
    std::vector<Tokens::Token> getTokens(const std::string & ns) const {
        auto it = tokens_.find(ns);
        if (it != tokens_.end()) {
            return it->second;
        }
        return {};
    }
    void setKeyWords(const std::unordered_map<std::string, Tokens::Type> & new_keywords) { keywords = new_keywords; }
    Tokens::Token nextToken() {
        skipWhitespaceAndComments();
        size_t start = pos();
        if (isAtEnd()) {
            return createToken(Tokens::Type::END_OF_FILE, start, start);
        }
        char c = peek();
        if (isalpha(c) || c == '_') {
            return matchIdentifierOrKeyword(start);
        }
        if (isdigit(c) || (isdigit(c) && peek(1) == '.')|| (c == '.' && isdigit(peek(1)))) {
            return matchNumber(start);
        }
        if (c == '"' || c == '\'') {
            return matchStringLiteral(start);
        }
        if (operators_.find(c) != std::string_view::npos) {
            return matchOperatorOrPunctuation(start);
        }
        advance();
        return createToken(Tokens::Type::UNKNOWN, start, pos());
    }
  private:
    std::unordered_map<std::string, std::string>                inputs_;
    std::unordered_map<std::string, std::vector<Tokens::Token>> tokens_;
    std::unordered_map<std::string, size_t>                     positions_;
    std::unordered_map<std::string, int>                        line_numbers_;
    std::unordered_map<std::string, int>                        column_numbers_;
    std::string                                   operators_;
    std::string                                   current_namespace_;
    std::unordered_map<std::string, Tokens::Type> keywords;
    // two chars
    static const std::vector<std::string> OPERATOR_RELATIONAL;
    static const std::vector<std::string> OPERATOR_INCREMENT;
    static const std::vector<std::string> OPERATOR_ASSIGNMENT;
    static const std::vector<std::string> OPERATOR_LOGICAL;
    // one char
    static const std::vector<std::string> OPERATOR_ARITHMETIC;
    static const std::vector<std::string> PUNCTUATION;
    const std::string & input() const { return inputs_.at(current_namespace_); }
    size_t & pos() { return positions_[current_namespace_]; }
    int & line() { return line_numbers_[current_namespace_]; }
    int & col() { return column_numbers_[current_namespace_]; }
    Tokens::Token createToken(Tokens::Type type, size_t start, size_t end, const std::string & value = "") {
        Tokens::Token token;
        token.type          = type;
        token.start_pos     = start;
        token.end_pos       = end;
        token.line_number   = line();
        token.column_number = col();
        if (start <= end && end <= input().length()) {
            token.lexeme = std::string_view(input()).substr(start, end - start);
            token.value  = value.empty() ? std::string(token.lexeme) : value;
        }
        return token;
    }
    // --------------------------------------
    char peek(size_t offset = 0) const {
        const auto & in = inputs_.at(current_namespace_);
        size_t       cp = positions_.at(current_namespace_);
        if (cp + offset >= in.length()) {
            return '\0';
        }
        return in[cp + offset];
    }
    char advance() {
        char c = peek();
        pos()++;
        if (c == '\n') {
            line()++;
            col() = 1;
        } else {
            col()++;
        }
        return c;
    }
    bool isAtEnd() const { return positions_.at(current_namespace_) >= inputs_.at(current_namespace_).length(); }
    bool isComment(const char current_char) const {
        return (current_char == '/' && peek(1) == '/' || current_char == '#');
    }
    void skipWhitespaceAndComments() {
        while (!isAtEnd()) {
            char c = peek();
            if (isspace(c)) {
                advance();
            } else if ((c == '/' && peek(1) == '/') || c == '#') {
                while (!isAtEnd() && peek() != '\n') {
                    advance();
                }
            } else {
                break;
            }
        }
    }
    Tokens::Token matchIdentifierOrKeyword(size_t start_pos, Tokens::Type type = Tokens::Type::IDENTIFIER) {
        while (!isAtEnd() && (isalnum(peek()) || peek() == '_')) {
            advance();
        }
        size_t      end   = pos();
        std::string value = input().substr(start_pos, end - start_pos);
        if (value.empty()) {
            return createToken(Tokens::Type::UNKNOWN, start_pos, end);
        }
        if (type == Tokens::Type::IDENTIFIER) {
            auto it = keywords.find(value);
            if (it != keywords.end()) {
                return createToken(it->second, start_pos, end);
            }
        }
        return createToken(type, start_pos, end);
    }
    Tokens::Token matchNumber(size_t start_pos) {
        bool has_dot = false;
        while (!isAtEnd()) {
            if (isdigit(peek())) {
                advance();
            } else if (!has_dot && peek() == '.' && isdigit(peek(1))) {
                has_dot = true;
                advance();  // a pont
                advance();  // az első számjegy a pont után
            } else {
                break;
            }
        }
        size_t end = pos();
        return createToken(Tokens::Type::NUMBER, start_pos, end);
    }
    Tokens::Token matchStringLiteral(size_t start_pos) {
        char opening_quote = peek();
        advance();  // Skip opening quote
        std::string value;
        bool        unterminated = false;
        while (!isAtEnd()) {
            char c = peek();
            if (c == opening_quote) {
                advance();
                break;
            }
            if (c == '\\') {
                advance();
                char e = advance();
                switch (e) {
                    case 'n':
                        value += '\n';
                        break;
                    case 't':
                        value += '\t';
                        break;
                    case '"':
                        value += opening_quote;
                        break;
                    case '\\':
                        value += '\\';
                        break;
                    default:
                        value += e;
                        break;
                }
            } else {
                value += advance();
            }
        }
        size_t end = pos();
        if (unterminated) {
            return createToken(Tokens::Type::UNKNOWN, start_pos, end, input().substr(start_pos, end - start_pos));
        }
        return createToken(Tokens::Type::STRING_LITERAL, start_pos, end, value);
    }
    Tokens::Token matchOperatorOrPunctuation(size_t start_pos) {
        char first_char = advance();  // Első karakter elfogyasztása
        if (!isAtEnd()) {
            char        second_char = peek(0);  // Következő karakter megnézése
            std::string two_chars_str{ first_char, second_char };
            const std::vector<std::pair<const std::vector<std::string> *, Tokens::Type>> two_char_op_types = {
                { &OPERATOR_RELATIONAL, Tokens::Type::OPERATOR_RELATIONAL },
                { &OPERATOR_INCREMENT,  Tokens::Type::OPERATOR_INCREMENT  },
                { &OPERATOR_ASSIGNMENT, Tokens::Type::OPERATOR_ASSIGNMENT },
                { &OPERATOR_LOGICAL,    Tokens::Type::OPERATOR_LOGICAL    }
            };
            for (const auto & [vec_ptr, type] : two_char_op_types) {
                if (matchFromVector(*vec_ptr, two_chars_str)) {
                    advance();  // Második karakter elfogyasztása
                    size_t end_pos = pos();
                    return createToken(type, start_pos, end_pos);
                }
            }
        }
        // Egykarakteres operátor vagy írásjel
        std::string single_char_str(1, first_char);
        if (single_char_str == "$") {
            if (isalpha(peek(0)) || peek(0) == '_') {
                return matchIdentifierOrKeyword(start_pos, Tokens::Type::VARIABLE_IDENTIFIER);
            }
        }
        const std::vector<std::pair<const std::vector<std::string> *, Tokens::Type>> one_char_op_types = {
            { &OPERATOR_ARITHMETIC, Tokens::Type::OPERATOR_ARITHMETIC },
            { &OPERATOR_ASSIGNMENT, Tokens::Type::OPERATOR_ASSIGNMENT }, // "=" itt van!
            { &PUNCTUATION,         Tokens::Type::PUNCTUATION         }
        };
        for (const auto & [vec_ptr, type] : one_char_op_types) {
            if (matchFromVector(*vec_ptr, single_char_str)) {
                size_t end_pos = pos();
                return createToken(type, start_pos, end_pos);
            }
        }
        size_t end_pos = pos();
        return createToken(Tokens::Type::UNKNOWN, start_pos, end_pos);
    }
    static bool matchFromVector(const std::vector<std::string> & vec, const std::string & value) {
        return std::find(vec.begin(), vec.end(), value) != vec.end();
    }
};  // class Lexer
};  // namespace Lexer
#endif  // LEXER_HPP
src/Lexer/Token.hpp
New file
@@ -0,0 +1,37 @@
#ifndef TOKEN_HPP
#define TOKEN_HPP
#include <iostream>
#include <string>               // <<< Hozzáadva
#include <string_view>          // <<< Hozzáadva
#include "Lexer/TokenType.hpp"  // Feltételezzük, hogy ez definiálja Type-ot és TypeToString-ot
namespace Lexer::Tokens {
struct Token {
    Lexer::Tokens::Type type;
    std::string         value;      // A token feldolgozott értéke
    std::string_view    lexeme;     // A nyers szövegrész az eredeti stringből
    size_t              start_pos;  // Kezdő index
    size_t              end_pos;    // Vég index (exclusive)
    int                 line_number;
    int                 column_number;
    // Módosított print metódus a lexeme kiírásával
    void print() const {
        std::cout << "Token { Type: " << Lexer::Tokens::TypeToString(type) << ", Value: \"" << value << "\""
                  << ", Pos: [" << start_pos << ", " << end_pos
                  << ")"
                  // Lexeme kiírása (stringgé konvertálva a biztonság kedvéért, ha pl. null lenne)
                  << ", Lexeme: \"" << std::string(lexeme) << "\""
                  << " }" << '\n';
    }
};
inline bool operator==(const Token & lhs, const Token & rhs) {
    return lhs.type == rhs.type && lhs.value == rhs.value && lhs.start_pos == rhs.start_pos &&
           lhs.end_pos == rhs.end_pos;
}
};  // namespace Lexer::Tokens
#endif  // TOKEN_HPP
src/Lexer/TokenType.hpp
New file
@@ -0,0 +1,87 @@
#ifndef TOKEN_TYPE_HPP
#define TOKEN_TYPE_HPP
#include <cstdint>
#include <string>
namespace Lexer::Tokens {
enum class Type : std::uint8_t {
    IDENTIFIER,           // Pl. változónév: myVar
    VARIABLE_IDENTIFIER,  // Pl. $
    KEYWORD,              // Pl. if, else, while
    NUMBER,               // Pl. 123, 42
    STRING_LITERAL,       // Pl. "hello world"
    OPERATOR_RELATIONAL,  // Pl. +, -, *, /, =, ==, <, >
    OPERATOR_LOGICAL,     // Pl. &&, ||
    OPERATOR_ASSIGNMENT,  // Pl. +=, -=, *=, /=
    OPERATOR_INCREMENT,   // Pl. ++, --
    OPERATOR_ARITHMETIC,  // Pl. +, -, *, /
    PUNCTUATION,          // Pl. (, ), {, }, ;, ,
    COMMENT,              // Általában kihagyjuk
    END_OF_FILE,          // A string végét jelzi
    KEYWORD_STRING,
    KEYWORD_INT,
    KEYWORD_DOUBLE,
    KEYWORD_FLOAT,
    KEYWORD_BOOLEAN,
    KEYWORD_FUNCTION,
    KEYWORD_RETURN,
    KEYWORD_NULL,
    UNKNOWN  // Ismeretlen karakter/szekvencia
};
inline std::string TypeToString(Lexer::Tokens::Type type) {
    switch (type) {
        case Lexer::Tokens::Type::IDENTIFIER:
            return "IDENTIFIER";
        case Lexer::Tokens::Type::VARIABLE_IDENTIFIER:
            return "VARIABLE_IDENTIFIER";
        case Lexer::Tokens::Type::KEYWORD:
            return "KEYWORD";
        case Lexer::Tokens::Type::NUMBER:
            return "NUMBER";
        case Lexer::Tokens::Type::STRING_LITERAL:
            return "STRING_LITERAL";
        case Lexer::Tokens::Type::OPERATOR_RELATIONAL:
            return "OPERATOR_RELATIONAL";
        case Lexer::Tokens::Type::OPERATOR_LOGICAL:
            return "OPERATOR_LOGICAL";
        case Lexer::Tokens::Type::OPERATOR_ASSIGNMENT:
            return "OPERATOR_ASSIGNMENT";
        case Lexer::Tokens::Type::OPERATOR_INCREMENT:
            return "OPERATOR_INCREMENT";
        case Lexer::Tokens::Type::OPERATOR_ARITHMETIC:
            return "OPERATOR_ARITHMETIC";
        case Lexer::Tokens::Type::PUNCTUATION:
            return "PUNCTUATION";
        case Lexer::Tokens::Type::COMMENT:
            return "COMMENT";
        case Lexer::Tokens::Type::END_OF_FILE:
            return "END_OF_FILE";
        case Lexer::Tokens::Type::KEYWORD_STRING:
            return "KEYWORD_STRING";
        case Lexer::Tokens::Type::KEYWORD_INT:
            return "KEYWORD_INT";
        case Lexer::Tokens::Type::KEYWORD_DOUBLE:
            return "KEYWORD_DOUBLE";
        case Lexer::Tokens::Type::KEYWORD_FLOAT:
            return "KEYWORD_FLOAT";
        case Lexer::Tokens::Type::KEYWORD_BOOLEAN:
            return "KEYWORD_BOOLEAN";
        case Lexer::Tokens::Type::KEYWORD_FUNCTION:
            return "KEYWORD_FUNCTION";
        case Lexer::Tokens::Type::KEYWORD_RETURN:
            return "KEYWORD_RETURN";
        case Lexer::Tokens::Type::KEYWORD_NULL:
            return "KEYWORD_NULL";
        case Lexer::Tokens::Type::UNKNOWN:
            return "UNKNOWN";
        default:
            return "INVALID_TYPE";
    }
}
};  // namespace Lexer::Tokens
#endif  // TOKEN_TYPE_HPP
src/Parser/Parser.cpp
New file
@@ -0,0 +1,37 @@
#include "Parser/Parser.hpp"
// Más szükséges include-ok, ha kellenek
namespace Parser {
const std::unordered_map<std::string, Lexer::Tokens::Type> Parser::keywords = {
    { "if",       Lexer::Tokens::Type::KEYWORD          },
    { "else",     Lexer::Tokens::Type::KEYWORD          },
    { "while",    Lexer::Tokens::Type::KEYWORD          },
    { "for",      Lexer::Tokens::Type::KEYWORD          },
    { "return",   Lexer::Tokens::Type::KEYWORD_RETURN   },
    { "function", Lexer::Tokens::Type::KEYWORD_FUNCTION },
    // Régebbiek:
    { "const",    Lexer::Tokens::Type::KEYWORD          },
    { "true",     Lexer::Tokens::Type::KEYWORD          },
    { "false",    Lexer::Tokens::Type::KEYWORD          },
    // változó típusok
    { "null",     Lexer::Tokens::Type::KEYWORD_NULL     },
    { "int",      Lexer::Tokens::Type::KEYWORD_INT      },
    { "double",   Lexer::Tokens::Type::KEYWORD_DOUBLE   },
    { "float",    Lexer::Tokens::Type::KEYWORD_FLOAT    },
    { "string",   Lexer::Tokens::Type::KEYWORD_STRING   },
    { "boolean",  Lexer::Tokens::Type::KEYWORD_BOOLEAN  },
    { "bool",     Lexer::Tokens::Type::KEYWORD_BOOLEAN  },
    // ... egyéb kulcsszavak ...
};
const std::unordered_map<Lexer::Tokens::Type, Symbols::Variables::Type> Parser::variable_types = {
    { Lexer::Tokens::Type::KEYWORD_INT,     Symbols::Variables::Type::INTEGER   },
    { Lexer::Tokens::Type::KEYWORD_DOUBLE,  Symbols::Variables::Type::DOUBLE    },
    { Lexer::Tokens::Type::KEYWORD_FLOAT,   Symbols::Variables::Type::FLOAT     },
    { Lexer::Tokens::Type::KEYWORD_STRING,  Symbols::Variables::Type::STRING    },
    { Lexer::Tokens::Type::KEYWORD_NULL,    Symbols::Variables::Type::NULL_TYPE },
    { Lexer::Tokens::Type::KEYWORD_BOOLEAN, Symbols::Variables::Type::BOOLEAN   },
};
}  // namespace Parser
src/Parser/Parser.hpp
New file
@@ -0,0 +1,536 @@
#ifndef PARSER_HPP
#define PARSER_HPP
#include <algorithm>
#include <memory>
#include <sstream>  // Hibaüzenetekhez
#include <stdexcept>
#include <string>
#include <vector>
// Szükséges header-ök a Lexer és Symbol komponensekből
#include "Lexer/Token.hpp"
#include "Lexer/TokenType.hpp"  // Enum és TypeToString
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/SymbolFactory.hpp"
#include "Symbols/Value.hpp"  // Symbols::Value miatt
namespace Parser {
class SyntaxError : public std::runtime_error {
  public:
    SyntaxError(const std::string & message, const int line, const int col) :
        std::runtime_error(message + " at line " + std::to_string(line) + ", column " + std::to_string(col)) {}
    SyntaxError(const std::string & message, const Lexer::Tokens::Token & token) :
        SyntaxError(
            message + " (found token: '" + token.value + "' type: " + Lexer::Tokens::TypeToString(token.type) + ")",
            token.line_number, token.column_number) {}
};
class Parser {
  public:
    Parser() {}
    void parseProgram(const std::vector<Lexer::Tokens::Token> & tokens, std::string_view input_string) {
        tokens_              = tokens;
        input_str_view_      = input_string;
        current_token_index_ = 0;
        symbol_container_    = std::make_unique<Symbols::SymbolContainer>();
        try {
            while (!isAtEnd() && currentToken().type != Lexer::Tokens::Type::END_OF_FILE) {
                parseStatement();
            }
            if (!isAtEnd() && currentToken().type != Lexer::Tokens::Type::END_OF_FILE) {
                reportError("Unexpected tokens after program end");
            }
        } catch (const SyntaxError & e) {
            std::cerr << "Syntax Error: " << e.what() << '\n';
        } catch (const std::exception & e) {
            std::cerr << "Error during parsing: " << e.what() << '\n';
            throw;
        }
    }
    const std::shared_ptr<Symbols::SymbolContainer> & getSymbolContainer() const {
        if (!symbol_container_) {
            throw std::runtime_error("Symbol container is not initialized.");
        }
        return symbol_container_;
    }
    static const std::unordered_map<std::string, Lexer::Tokens::Type>              keywords;
    static const std::unordered_map<Lexer::Tokens::Type, Symbols::Variables::Type> variable_types;
  private:
    std::vector<Lexer::Tokens::Token>         tokens_;
    std::string_view                          input_str_view_;
    size_t                                    current_token_index_;
    std::shared_ptr<Symbols::SymbolContainer> symbol_container_;
    // Token Stream Kezelő és Hibakezelő segédfüggvények (változatlanok)
    const Lexer::Tokens::Token & currentToken() const {
        if (isAtEnd()) {
            // Technikailag itt már nem kellene lennünk, ha a parseProgram ciklus jól van megírva
            // De biztonsági ellenőrzésként jó lehet
            if (!tokens_.empty() && tokens_.back().type == Lexer::Tokens::Type::END_OF_FILE) {
                return tokens_.back();  // Visszaadjuk az EOF tokent
            }
            throw std::runtime_error("Unexpected end of token stream reached.");
        }
        return tokens_[current_token_index_];
    }
    // Előre néz a token stream-ben
    const Lexer::Tokens::Token & peekToken(size_t offset = 1) const {
        if (current_token_index_ + offset >= tokens_.size()) {
            // EOF vagy azon túl vagyunk, adjuk vissza az utolsó tokent (ami EOF kell legyen)
            if (!tokens_.empty()) {
                return tokens_.back();
            }
            throw std::runtime_error("Cannot peek beyond end of token stream.");
        }
        return tokens_[current_token_index_ + offset];
    }
    // Elfogyasztja (lépteti az indexet) az aktuális tokent és visszaadja azt
    Lexer::Tokens::Token consumeToken() {
        if (isAtEnd()) {
            throw std::runtime_error("Cannot consume token at end of stream.");
        }
        return tokens_[current_token_index_++];
    }
    // Ellenőrzi, hogy az aktuális token típusa megegyezik-e a várttal.
    // Ha igen, elfogyasztja és true-t ad vissza. Ha nem, false-t ad vissza.
    bool match(Lexer::Tokens::Type expected_type) {
        if (isAtEnd()) {
            return false;
        }
        if (currentToken().type == expected_type) {
            consumeToken();
            return true;
        }
        return false;
    }
    // Ellenőrzi, hogy az aktuális token típusa és értéke megegyezik-e a várttal.
    // Csak OPERATOR és PUNCTUATION esetén érdemes használni az érték ellenőrzést.
    bool match(Lexer::Tokens::Type expected_type, const std::string & expected_value) {
        if (isAtEnd()) {
            return false;
        }
        const auto & token = currentToken();
        if (token.type == expected_type && token.value == expected_value) {
            consumeToken();
            return true;
        }
        return false;
    }
    Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type) {
        if (isAtEnd()) {
            reportError("Unexpected end of file, expected token type: " + Lexer::Tokens::TypeToString(expected_type));
        }
        const auto & token = currentToken();
        if (token.type == expected_type) {
            return consumeToken();
        }
        reportError("Expected token type " + Lexer::Tokens::TypeToString(expected_type));
        // A reportError dob, ez a return sosem fut le, de a fordító kedvéért kellhet:
        return token;  // Vagy dobjon a reportError
    }
    // Mint az expect, de az értékét is ellenőrzi.
    Lexer::Tokens::Token expect(Lexer::Tokens::Type expected_type, const std::string & expected_value) {
        if (isAtEnd()) {
            reportError("Unexpected end of file, expected token: " + Lexer::Tokens::TypeToString(expected_type) +
                        " with value '" + expected_value + "'");
        }
        const auto & token = currentToken();
        if (token.type == expected_type && token.value == expected_value) {
            return consumeToken();
        }
        reportError("Expected token " + Lexer::Tokens::TypeToString(expected_type) + " with value '" + expected_value +
                    "'");
        return token;  // reportError dob
    }
    // Ellenőrzi, hogy a releváns tokenek végére értünk-e (az EOF előtti utolsó tokenen vagyunk-e)
    bool isAtEnd() const {
        // Akkor vagyunk a végén, ha az index a tokenek méretével egyenlő,
        // vagy ha már csak az EOF token van hátra (ha az a lista utolsó eleme).
        return current_token_index_ >= tokens_.size() ||
               (current_token_index_ == tokens_.size() - 1 && tokens_.back().type == Lexer::Tokens::Type::END_OF_FILE);
    }
    // --- Hibakezelés ---
    // Hiba jelentése és kivétel dobása
    [[noreturn]] void reportError(const std::string & message) {
        // Használjuk az aktuális token pozícióját, ha még nem értünk a végére
        if (current_token_index_ < tokens_.size()) {
            throw SyntaxError(message, tokens_[current_token_index_]);
        }  // Ha már a végén vagyunk, az utolsó ismert pozíciót használjuk
        int line = tokens_.empty() ? 0 : tokens_.back().line_number;
        int col  = tokens_.empty() ? 0 : tokens_.back().column_number;
        throw SyntaxError(message, line, col);
    }
    // --- Elemzési Módszerek (Moduláris részek) ---
    // parseStatement (változatlan)
    void parseStatement() {
        const auto & token_type = currentToken().type;
        if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION) {
            parseFunctionDefinition();
            return;
        }
        for (const auto & _type : Parser::Parser::variable_types) {
            if (token_type == _type.first) {
                parseVariableDefinition();
                return;
            }
        }
        reportError("Unexpected token at beginning of statement");
    }
    // parseVariableDefinition (SymbolFactory használata már korrekt volt)
    void parseVariableDefinition() {
        Symbols::Variables::Type var_type_enum = parseType();
        // A típus stringjének tárolása csak a debug kiíráshoz kell
        //std::string              var_type_str  = tokens_[current_token_index_ - 1].value;
        std::cout << "var_name: " << currentToken().lexeme << std::endl;
        Lexer::Tokens::Token id_token = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER);
        std::string          var_name = id_token.value;
        // Levágjuk a '$' jelet, ha a lexer mégis benne hagyta
        if (!var_name.empty() && var_name[0] == '$') {
            var_name = var_name.substr(1);
        }
        expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "=");
        Symbols::Value initial_value = parseValue(var_type_enum);
        expect(Lexer::Tokens::Type::PUNCTUATION, ";");
        std::string context  = "global";  // Globális változó
        // SymbolFactory használata a létrehozáshoz
        auto variable_symbol = Symbols::SymbolFactory::createVariable(var_name, initial_value, context, var_type_enum);
        // Ellenőrzés és definíció az *aktuális* scope-ban (ami itt a globális)
        if (symbol_container_->exists("variables", var_name)) {
            reportError("Variable '" + var_name + "' already defined in this scope");
        }
        symbol_container_->define("variables", variable_symbol);
        // Debugging kiírás (változatlan)
        // std::cout << "Parsed variable: " << var_type_str << " " << id_token.value << " = ... ;\n";
    }
    // *** MÓDOSÍTOTT parseFunctionDefinition ***
    void parseFunctionDefinition() {
        expect(Lexer::Tokens::Type::KEYWORD_FUNCTION);
        Lexer::Tokens::Token     id_token         = expect(Lexer::Tokens::Type::IDENTIFIER);
        std::string              func_name        = id_token.value;
        Symbols::Variables::Type func_return_type = Symbols::Variables::Type::NULL_TYPE;
        expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "=");
        expect(Lexer::Tokens::Type::PUNCTUATION, "(");
        Symbols::FunctionParameterInfo param_infos;
        if (currentToken().type != Lexer::Tokens::Type::PUNCTUATION || currentToken().value != ")") {
            while (true) {
                // Paraméter típusa
                //size_t                   type_token_index = current_token_index_;  // Elmentjük a típus token indexét
                Symbols::Variables::Type param_type = parseType();  // Ez elfogyasztja a type tokent
                // Paraméter név ($variable)
                Lexer::Tokens::Token param_id_token = expect(Lexer::Tokens::Type::VARIABLE_IDENTIFIER);
                std::string          param_name     = param_id_token.value;
                if (!param_name.empty() && param_name[0] == '$') {  // '$' eltávolítása
                    param_name = param_name.substr(1);
                }
                param_infos.push_back({ param_name, param_type });
                // Vessző vagy zárójel következik?
                if (match(Lexer::Tokens::Type::PUNCTUATION, ",")) {
                    continue;
                }
                if (currentToken().type == Lexer::Tokens::Type::PUNCTUATION && currentToken().value == ")") {
                    break;  // Lista vége
                }
                reportError("Expected ',' or ')' in parameter list");
            }
        }
        // Most a ')' következik
        expect(Lexer::Tokens::Type::PUNCTUATION, ")");
        // check if we have a option return type: function name() type { ... }
        for (const auto & _type : Parser::variable_types) {
            if (match(_type.first)) {
                func_return_type = _type.second;
                break;
            }
        }
        auto function_symbol =
            Symbols::SymbolFactory::createFunction(func_name, "global", param_infos, "", func_return_type);
        if (symbol_container_->exists("functions", func_name)) {
            reportError("Function '" + func_name + "' already defined in this scope");
        }
        Lexer::Tokens::Token opening_brace = expect(Lexer::Tokens::Type::PUNCTUATION, "{");
        symbol_container_->define("functions", function_symbol);
        // only parse the body if we checked out if not exists the function and created the symbol
        Symbols::SymbolContainer func_symbols =
            parseFunctionBody(opening_brace, func_return_type != Symbols::Variables::Type::NULL_TYPE);
        // create new container for the function
        std::cout << "Defined function symbol: " << func_name << " in global scope.\n";
        // 3. Új scope nyitása a függvény paraméterei (és később lokális változói) számára
        symbol_container_->enterScope();
        //std::cout << "Entered scope for function: " << func_name << "\n";
        // 4. Paraméterek definiálása mint változók az *új* (függvény) scope-ban
        const std::string & param_context = func_name;  // Paraméter kontextusa a függvény neve
        for (const auto & p_info : param_infos) {
            auto param_symbol = Symbols::SymbolFactory::createVariable(p_info.name,       // Név '$' nélkül
                                                                       Symbols::Value(),  // Alapértelmezett/üres érték
                                                                       param_context,     // Kontextus
                                                                       p_info.type        // Típus
            );
            if (symbol_container_->exists("variables", p_info.name)) {
                reportError("Parameter name '" + p_info.name + "' conflicts with another symbol in function '" +
                            func_name + "'");
            }
            symbol_container_->define("variables", param_symbol);
        }
        // 5. Függvény scope elhagyása
        symbol_container_->leaveScope();
        //std::cout << "Left scope for function: " << func_name << "\n";
        // Debugging kiírás (változatlan)
        // std::cout << "Parsed function: " << func_name << " (...) { ... } scope handled.\n";
    }
    // --- Elemzési Segédfüggvények ---
    // type : KEYWORD_STRING | KEYWORD_INT | KEYWORD_DOUBLE
    // Visszaadja a megfelelő Symbols::Variables::Type enum értéket és elfogyasztja a tokent.
    Symbols::Variables::Type parseType() {
        const auto & token = currentToken();
        for (const auto & _type : Parser::variable_types) {
            if (token.type == _type.first) {
                consumeToken();
                return _type.second;
            }
        }
        reportError("Expected type keyword (string, int, double, float)");
    }
    // value : STRING_LITERAL | NUMBER
    // Feldolgozza az értéket a várt típus alapján.
    Symbols::Value parseValue(Symbols::Variables::Type expected_var_type) {
        Lexer::Tokens::Token token = currentToken();
        /// find if this is a function call
        if (token.type == Lexer::Tokens::Type::IDENTIFIER && peekToken().lexeme == "(") {
            for (const auto & symbol_ptr : this->symbol_container_->listNamespace("functions")) {
                if (auto func_symbol = std::dynamic_pointer_cast<Symbols::FunctionSymbol>(symbol_ptr)) {
                    // TODO: A függvény hívását kellene feldolgozni, a func_symbol-ból kellene lekérni a paramétereket, func_symbol->plainBody() tartalmazza a függvény törzsét
                    // A függvény hívásának feldolgozása
                    std::cout << "Function call: " << token.value << "\n";
                    while (consumeToken().lexeme != ")") {
                        // Feltételezzük, hogy a függvény hívását a lexer már feldolgozta
                    }
                    return Symbols::Value("");  // TODO: Implementálni a függvény hívását
                }
            }
        }
        bool is_negative = false;
        std::cout << "Peek: " << peekToken().lexeme << "\n";
        if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC && peekToken().type == Lexer::Tokens::Type::NUMBER) {
            is_negative = true;
            token       = peekToken();
            consumeToken();
        }
        if (expected_var_type == Symbols::Variables::Type::STRING) {
            if (token.type == Lexer::Tokens::Type::STRING_LITERAL) {
                consumeToken();
                return Symbols::Value(token.value);  // A lexer value-ja már a feldolgozott string
            }
            reportError("Expected string literal value");
        } else if (expected_var_type == Symbols::Variables::Type::INTEGER) {
            if (token.type == Lexer::Tokens::Type::NUMBER) {
                // Konvertálás int-re, hibakezeléssel
                try {
                    // TODO: Ellenőrizni, hogy a szám valóban egész-e (nincs benne '.')
                    // Most egyszerűen std::stoi-t használunk.
                    int int_value = std::stoi(token.value);
                    if (is_negative) {
                        int_value = -int_value;
                    }
                    consumeToken();
                    return Symbols::Value(int_value);
                } catch (const std::invalid_argument & e) {
                    reportError("Invalid integer literal: " + token.value);
                } catch (const std::out_of_range & e) {
                    reportError("Integer literal out of range: " + token.value);
                }
            }
            reportError("Expected integer literal value");
        } else if (expected_var_type == Symbols::Variables::Type::DOUBLE) {
            if (token.type == Lexer::Tokens::Type::NUMBER) {
                // Konvertálás double-re, hibakezeléssel
                try {
                    double double_value = std::stod(token.value);
                    if (is_negative) {
                        double_value = -double_value;
                    }
                    consumeToken();
                    return Symbols::Value(double_value);
                } catch (const std::invalid_argument & e) {
                    reportError("Invalid double literal: " + token.value);
                } catch (const std::out_of_range & e) {
                    reportError("Double literal out of range: " + token.value);
                }
            }
            reportError("Expected numeric literal value for double");
        } else if (expected_var_type == Symbols::Variables::Type::FLOAT) {
            if (token.type == Lexer::Tokens::Type::NUMBER) {
                // Konvertálás double-re, hibakezeléssel
                try {
                    float float_value = std::atof(token.value.data());
                    if (is_negative) {
                        float_value = -float_value;
                    }
                    consumeToken();
                    return Symbols::Value(float_value);
                } catch (const std::invalid_argument & e) {
                    reportError("Invalid float literal: " + token.value);
                } catch (const std::out_of_range & e) {
                    reportError("Float literal out of range: " + token.value);
                }
            }
            reportError("Expected numeric literal value for double");
        } else if (expected_var_type == Symbols::Variables::Type::BOOLEAN) {
            if (token.type == Lexer::Tokens::Type::KEYWORD) {
                consumeToken();
                return Symbols::Value(token.value == "true");  // A lexer value-ja már a feldolgozott string
            }
            reportError("Expected boolean literal value");
        } else {
            // Más típusok (pl. boolean) itt kezelendők, ha lennének
            reportError("Unsupported variable type encountered during value parsing");
        }
        // Should not reach here due to reportError throwing
        return Symbols::Value();  // Default return to satisfy compiler
    }
    Symbols::SymbolContainer parseFunctionBody(const Lexer::Tokens::Token & opening_brace,
                                               bool                         return_required = false) {
        size_t               braceDepth = 0;
        int                  tokenIndex = current_token_index_;
        Lexer::Tokens::Token currentToken_;
        Lexer::Tokens::Token closing_brace;
        while (tokenIndex < tokens_.size()) {
            currentToken_ = peekToken();
            if (currentToken_.type == Lexer::Tokens::Type::PUNCTUATION) {
                if (currentToken_.value == "{") {
                    ++braceDepth;
                } else if (currentToken_.value == "}") {
                    if (braceDepth == 0) {
                        closing_brace = currentToken_;
                        break;
                    }
                    --braceDepth;
                }
            }
        }
        if (braceDepth != 0) {
            reportError("Unmatched braces in function body");
        }
        std::vector<Lexer::Tokens::Token> filtered_tokens;
        auto                              startIt = std::find(tokens_.begin(), tokens_.end(), opening_brace);
        auto                              endIt   = std::find(tokens_.begin(), tokens_.end(), closing_brace);
        // Ellenőrzés: mindkét token megtalálva és start_token megelőzi az end_token-t
        if (startIt != tokens_.end() && endIt != tokens_.end() && startIt < endIt) {
            filtered_tokens = std::vector<Lexer::Tokens::Token>(startIt + 1, endIt);
        }
        std::string_view input_string = input_str_view_.substr(opening_brace.end_pos, closing_brace.end_pos);
        auto             parser       = Parser();
        parser.parseProgram(filtered_tokens, input_string);
        return *parser.getSymbolContainer();
    }
    std::string parseFunctionBodyOld(size_t body_start_pos, bool return_required = false) {
        std::stringstream body_ss;
        int               brace_level        = 1;
        size_t            last_token_end_pos = body_start_pos;
        bool              has_return         = false;
        while (true) {
            if (isAtEnd()) {
                reportError("Unexpected end of file inside function body (missing '}')");
            }
            const auto & token = currentToken();
            // Whitespace visszaállítása (ha volt) az 'input_str_view_' alapján
            if (token.start_pos > last_token_end_pos) {
                if (last_token_end_pos < input_str_view_.length() && token.start_pos <= input_str_view_.length()) {
                    body_ss << input_str_view_.substr(last_token_end_pos, token.start_pos - last_token_end_pos);
                } else {
                    reportError("Invalid position range in body reconstruction");
                }
            }
            if (token.type == Lexer::Tokens::Type::KEYWORD_RETURN) {
                has_return = true;
            }
            if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.value == "{") {
                brace_level++;
                body_ss << token.lexeme;
            } else if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.value == "}") {
                brace_level--;
                if (brace_level == 0) {
                    consumeToken();  // Záró '}' elfogyasztása
                    break;
                }
                body_ss << token.lexeme;
            } else {
                body_ss << token.lexeme;
            }
            last_token_end_pos = token.end_pos;
            consumeToken();
        }
        if (return_required && !has_return) {
            return "";
        }
        return body_ss.str();
    }
};  // class Parser
}  // namespace Parser
#endif  // PARSER_HPP
src/ScriptException.hpp
File was deleted
src/ScriptExceptionMacros.h
File was deleted
src/ScriptInterpreter.cpp
File was deleted
src/ScriptInterpreter.hpp
File was deleted
src/ScriptInterpreterHelpers.hpp
File was deleted
src/StringHelpers.hpp
File was deleted
src/Symbols/BaseSymbol.hpp
File was renamed from include/BaseSymbol.hpp
@@ -3,13 +3,11 @@
#define BASE_SYMBOL_HPP
#include <string>
#include <variant>
#include "SymbolKind.hpp"
#include "Value.hpp"
namespace Symbols {
using Value = std::variant<int, double, std::string, bool>;
class Symbol {
  protected:
src/Symbols/ConstantSymbol.hpp
src/Symbols/FunctionSymbol.hpp
New file
@@ -0,0 +1,41 @@
// FunctionSymbol.hpp
#ifndef FUNCTION_SYMBOL_HPP
#define FUNCTION_SYMBOL_HPP
#include <vector>
#include "BaseSymbol.hpp"
#include "Lexer/Token.hpp"
#include "Symbols/ParameterContainer.hpp"
#include "Symbols/VariableTypes.hpp"
namespace Symbols {
class FunctionSymbol : public Symbol {
    // store the variables name and type
    FunctionParameterInfo             parameters_;
    Symbols::Variables::Type          returnType_;
    std::string                       plainBody_;
    std::vector<Lexer::Tokens::Token> tokens_;
  public:
    FunctionSymbol(const std::string & name, const std::string & context, const FunctionParameterInfo & parameters,
                   const std::string &      plainbody  = "",
                   Symbols::Variables::Type returnType = Symbols::Variables::Type::NULL_TYPE) :
        Symbol(name, {}, context, Symbols::Kind::Function),
        parameters_(parameters),
        plainBody_(plainbody),
        returnType_(returnType) {}
    Symbols::Kind kind() const override { return Symbols::Kind::Function; }
    Symbols::Variables::Type returnType() const { return returnType_; }
    const FunctionParameterInfo & parameters() const { return parameters_; }
    const std::string & plainBody() const { return plainBody_; }
};
}  // namespace Symbols
#endif
src/Symbols/ParameterContainer.hpp
New file
@@ -0,0 +1,17 @@
#ifndef SYMBOLS_PARAMETER_CONTAINER_HPP
#define SYMBOLS_PARAMETER_CONTAINER_HPP
#include <string>
#include <vector>
#include "Symbols/VariableTypes.hpp"
namespace Symbols {
struct functionParameterType {
    std::string              name;
    Symbols::Variables::Type type;
};
using FunctionParameterInfo = std::vector<Symbols::functionParameterType>;
}  // namespace Symbols
#endif
src/Symbols/SymbolContainer.hpp
src/Symbols/SymbolFactory.hpp
File was renamed from include/SymbolFactory.hpp
@@ -24,32 +24,42 @@
    }
    static std::shared_ptr<Symbol> createFunction(const std::string & name, const std::string & context,
                                                  const Symbols::ValueContainer & parameters = {}) {
                                                  const Symbols::FunctionParameterInfo & parameters = {}) {
        return std::make_shared<FunctionSymbol>(name, context, parameters);
    }
    static std::shared_ptr<Symbol> createFunction(const std::string & name, const std::string & context,
                                                  const Symbols::ValueContainer & parameters,
                                                  const std::string &             plainBody) {
                                                  const Symbols::FunctionParameterInfo & parameters,
                                                  const std::string &                    plainBody) {
        return std::make_shared<FunctionSymbol>(name, context, parameters, plainBody);
    }
    static std::shared_ptr<Symbol> createFunction(const std::string & name, const std::string & context,
                                                  const Symbols::FunctionParameterInfo & parameters,
                                                  const std::string & plainBody, Symbols::Variables::Type returnType) {
        return std::make_shared<FunctionSymbol>(name, context, parameters, plainBody, returnType);
    }
    // Overloadok
    static std::shared_ptr<Symbol> createVariable(const std::string & name, int value, const std::string & context) {
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::VT_INT);
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::INTEGER);
    }
    static std::shared_ptr<Symbol> createVariable(const std::string & name, double value, const std::string & context) {
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::VT_DOUBLE);
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::DOUBLE);
    }
    static std::shared_ptr<Symbol> createVariable(const std::string & name, float value, const std::string & context) {
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::FLOAT);
    }
    static std::shared_ptr<Symbol> createVariable(const std::string & name, const std::string & value,
                                                  const std::string & context) {
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::VT_STRING);
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::STRING);
    }
    static std::shared_ptr<Symbol> createVariable(const std::string & name, bool value, const std::string & context) {
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::VT_BOOLEAN);
        return createVariable(name, Symbols::Value(value), context, Symbols::Variables::Type::BOOLEAN);
    }
};
src/Symbols/SymbolKind.hpp
src/Symbols/SymbolTable.hpp
File was renamed from include/SymbolTable.hpp
@@ -3,7 +3,6 @@
#define SYMBOL_TABLE_HPP
#include <memory>
#include <optional>
#include <vector>
#include "SymbolTypes.hpp"
src/Symbols/SymbolTypes.hpp
src/Symbols/Value.hpp
New file
@@ -0,0 +1,77 @@
#ifndef SYMBOL_VALUE_HPP
#define SYMBOL_VALUE_HPP
#include <iostream>
#include <stdexcept>
#include <string>
#include <variant>
namespace Symbols {
class Value {
  public:
    using Variant = std::variant<int, double, float, std::string, bool>;
    Value() = default;
    Value(int v) : value_(v) {}
    Value(double v) : value_(v) {}
    Value(float v) : value_(v) {}
    Value(const std::string & v) : value_(v) {}
    Value(const char * v) : value_(std::string(v)) {}
    Value(bool v) : value_(v) {}
    const Variant & get() const { return value_; }
    Variant & get() { return value_; }
    template <typename T> T get() const { return std::get<T>(value_); }
    // operator+
    friend Value operator+(const Value & lhs, const Value & rhs) {
        return std::visit(
            [](auto && a, auto && b) -> Value {
                using A = std::decay_t<decltype(a)>;
                using B = std::decay_t<decltype(b)>;
                if constexpr ((std::is_arithmetic_v<A> && std::is_arithmetic_v<B>) ) {
                    return Value{ a + b };
                } else {
                    throw std::runtime_error("Invalid types for operator+");
                }
            },
            lhs.value_, rhs.value_);
    }
    // operator==
    friend bool operator==(const Value & lhs, const Value & rhs) { return lhs.value_ == rhs.value_; }
    // to_string helper (needed for string + int, etc.)
    static std::string to_string(const Variant & v) {
        return std::visit(
            [](auto && val) -> std::string {
                using T = std::decay_t<decltype(val)>;
                if constexpr (std::is_same_v<T, bool>) {
                    return val ? "true" : "false";
                } else if constexpr (std::is_same_v<T, std::string>) {
                    return val;
                } else {
                    return std::to_string(val);
                }
            },
            v);
    }
    static std::string to_string(const Value & val) { return to_string(val.value_); }
  private:
    Variant value_;
};
}  // namespace Symbols
#endif  // SYMBOL_VALUE_HPP
src/Symbols/ValueContainer.hpp
New file
@@ -0,0 +1,12 @@
#ifndef SYMBOLS_VALUE_CONTAINER_HPP
#define SYMBOLS_VALUE_CONTAINER_HPP
#include <vector>
#include "Symbols/Value.hpp"
namespace Symbols {
using ValueContainer = std::vector<Symbols::Value>;
};  // namespace Symbols
#endif  // SYMBOLS_VALUE_CONTAINER_HPP
src/Symbols/VariableSymbol.hpp
New file
@@ -0,0 +1,45 @@
// VariableSymbol.hpp
#ifndef VARIABLE_SYMBOL_HPP
#define VARIABLE_SYMBOL_HPP
#include "BaseSymbol.hpp"
#include "Symbols/VariableTypes.hpp"
namespace Symbols {
class VariableSymbol : public Symbol {
  protected:
    Symbols::Variables::Type type_;
  public:
    VariableSymbol(const std::string & name, const Symbols::Value & value, const std::string & context,
                   Variables::Type type) :
        Symbol(name, value, context, Symbols::Kind::Variable),
        type_(type) {}
    Symbols::Kind kind() const override { return Symbols::Kind::Variable; }
    Variables::Type type() const { return type_; }
    std::string toString() const {
        std::string r = "VariableSymbol: " + name_ + " Type: " + Symbols::Variables::TypeToString(type_);
        if (type_ == Symbols::Variables::Type::INTEGER) {
            r += " Value: " + std::to_string(value_.get<int>());
        } else if (type_ == Symbols::Variables::Type::DOUBLE) {
            r += " Value: " + std::to_string(value_.get<double>());
        } else if (type_ == Symbols::Variables::Type::FLOAT) {
            r += " Value: " + std::to_string(value_.get<float>());
        } else if (type_ == Symbols::Variables::Type::STRING) {
            r += " Value: " + value_.get<std::string>();
        } else if (type_ == Symbols::Variables::Type::BOOLEAN) {
            r += " Value: " + std::to_string(value_.get<bool>());
        } else if (type_ == Symbols::Variables::Type::NULL_TYPE) {
            r += " Value: null";
        }
        return r;
    }
};
}  // namespace Symbols
#endif
src/Symbols/VariableTypes.hpp
New file
@@ -0,0 +1,48 @@
#ifndef VARIABLE_TYPES_HPP
#define VARIABLE_TYPES_HPP
#include <cstdint>
#include <string>
#include <unordered_map>
namespace Symbols::Variables {
enum class Type : std::uint8_t { INTEGER, DOUBLE, FLOAT, STRING, BOOLEAN, NULL_TYPE, UNDEFINED_TYPE };
const std::unordered_map<std::string, Type> StringToTypeMap = {
    { "int",       Type::INTEGER        },
    { "double",    Type::DOUBLE         },
    { "float",     Type::FLOAT          },
    { "string",    Type::STRING         },
    { "bool",      Type::BOOLEAN        },
    { "boolean",   Type::BOOLEAN        },
    { "null",      Type::NULL_TYPE      },
    { "undefined", Type::UNDEFINED_TYPE },
};
const std::unordered_map<Type, std::string> StypeToStringMap = {
    { Type::INTEGER,        "int"        },
    { Type::DOUBLE,         "double"     },
    { Type::FLOAT,          "float"      },
    { Type::STRING,         "string"     },
    { Type::BOOLEAN,        "bool"       },
    { Type::NULL_TYPE,      "null"       },
    { Type::UNDEFINED_TYPE, "undeffined" },
};
inline static std::string TypeToString(Symbols::Variables::Type type) {
    if (StypeToStringMap.find(type) != StypeToStringMap.end()) {
        return StypeToStringMap.at(type);
    }
    return "null";
};
inline static Type StringToType(const std::string & type) {
    if (StringToTypeMap.find(type) != StringToTypeMap.end()) {
        return StringToTypeMap.at(type);
    }
    return Type::NULL_TYPE;
};
};  // namespace Symbols::Variables
#endif  // VARIABLE_TYPES_HPP
src/Token.hpp
File was deleted
src/VoidScript.hpp
New file
@@ -0,0 +1,89 @@
#ifndef VOIDSCRIPT_HPP
#define VOIDSCRIPT_HPP
#include <filesystem>
#include <fstream>
#include <string>
#include "Lexer/Lexer.hpp"
#include "Parser/Parser.hpp"
class VoidScript {
  private:
    std::string                     file;
    std::shared_ptr<Lexer::Lexer>   lexer  = nullptr;
    std::shared_ptr<Parser::Parser> parser = nullptr;
    std::string                     file_content;
  public:
    VoidScript(const std::string & file) : file(file) {
        if (!std::filesystem::exists(file)) {
            throw std::runtime_error("File " + file + " does not exits");
        }
        lexer  = std::make_shared<Lexer::Lexer>();
        parser = std::make_shared<Parser::Parser>();
        lexer->setKeyWords(Parser::Parser::keywords);
        // read in the file
        std::ifstream input(file, std::ios::in);
        if (!input.is_open()) {
            throw std::runtime_error("Could not open file " + file);
        }
        file_content = std::string((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
        input.close();
    }
    int run() {
        try {
            this->lexer->addNamespaceInput(this->file, this->file_content);
            const auto tokens = this->lexer->tokenizeNamespace(this->file);
            std::cout << "--- Tokens ---" << '\n';
            for (const auto & token : tokens) {
                if (token.type != Lexer::Tokens::Type::END_OF_FILE) {
                    //token.print();
                }
            }
            std::cout << "--------------" << '\n';
            parser->parseProgram(tokens, this->file_content);
            const std::shared_ptr<Symbols::SymbolContainer> & symbol_container = parser->getSymbolContainer();
            std::cout << "\n--- Defined Variables ---" << '\n';
            for (const auto & symbol_ptr : symbol_container->listNamespace("variables")) {
                // Itt lehetne a szimbólumokat kiírni vagy tovább feldolgozni
                // Szükséges lehet dynamic_cast<> a konkrét típushoz (VariableSymbol)
                if (auto var_symbol = std::dynamic_pointer_cast<Symbols::VariableSymbol>(symbol_ptr)) {
                    std::cout << var_symbol->toString() << '\n';
                }
            }
            std::cout << "\n--- Defined Functions ---" << '\n';
            for (const auto & symbol_ptr : symbol_container->listNamespace("functions")) {
                if (auto func_symbol = std::dynamic_pointer_cast<Symbols::FunctionSymbol>(symbol_ptr)) {
                    std::cout << "Func Name: " << func_symbol->name()
                              << " return type: " << Symbols::Variables::TypeToString(func_symbol->returnType())
                              << '\n';
                    for (const auto & func_param : func_symbol->parameters()) {
                        std::cout << "  Param: " << func_param.name
                                  << " Type: " << Symbols::Variables::TypeToString(func_param.type) << '\n';
                    }
                    std::cout << "  Context name: " << func_symbol->context() << '\n';
                    std::cout << "  Plain body: " << func_symbol->plainBody() << '\n';
                }
            }
            return 0;
        } catch (const Parser::SyntaxError & e) {
            std::cerr << "Syntax Error during parsing: " << e.what() << '\n';
            return 1;
        } catch (const std::exception & e) {
            std::cerr << "An error occurred: " << e.what() << '\n';
            return 1;
        }
        return 1;
    }
};  // class VoidScript
#endif  // VOIDSCRIPT_HPP