A simple scripting language in C++
Ferenc Szontágh
2025-04-17 ba9a9199d01b0fdd4bf9a54914f8058bf71f30c5
unary and binary operations
16 files modified
13 files added
1867 ■■■■ changed files
.clangd 2 ●●● patch | view | raw | blame | history
cli/main.cpp 3 ●●●●● patch | view | raw | blame | history
src/!NameSpaceManager.hpp 144 ●●●●● patch | view | raw | blame | history
src/Interpreter/BinaryExpressionNode.hpp 68 ●●●●● patch | view | raw | blame | history
src/Interpreter/DeclareFunctionStatementNode.hpp 55 ●●●●● patch | view | raw | blame | history
src/Interpreter/DeclareVariableStatementNode.hpp 52 ●●●●● patch | view | raw | blame | history
src/Interpreter/ExpressionBuilder.hpp 42 ●●●●● patch | view | raw | blame | history
src/Interpreter/ExpressionNode.hpp 11 ●●●●● patch | view | raw | blame | history
src/Interpreter/IdentifierExpressionNode.hpp 29 ●●●●● patch | view | raw | blame | history
src/Interpreter/Interpreter.hpp 62 ●●●●● patch | view | raw | blame | history
src/Interpreter/LiteralExpressionNode.hpp 25 ●●●●● patch | view | raw | blame | history
src/Interpreter/Operation.hpp 66 ●●●● patch | view | raw | blame | history
src/Interpreter/OperationContainer.hpp 129 ●●●●● patch | view | raw | blame | history
src/Interpreter/OperationsFactory.hpp 59 ●●●●● patch | view | raw | blame | history
src/Interpreter/StatementNode.hpp 26 ●●●●● patch | view | raw | blame | history
src/Interpreter/UnaryExpressionNode.hpp 64 ●●●●● patch | view | raw | blame | history
src/Interpreter/VariableExpressionNode.hpp 36 ●●●●● patch | view | raw | blame | history
src/Interpreter/VariableReferenceNode.hpp 37 ●●●●● patch | view | raw | blame | history
src/Lexer/Lexer.hpp 63 ●●●● patch | view | raw | blame | history
src/Lexer/TokenType.hpp 4 ●●●● patch | view | raw | blame | history
src/Parser/ParsedExpression.hpp 66 ●●●●● patch | view | raw | blame | history
src/Parser/Parser.cpp 2 ●●● patch | view | raw | blame | history
src/Parser/Parser.hpp 452 ●●●●● patch | view | raw | blame | history
src/Symbols/BaseSymbol.hpp 11 ●●●●● patch | view | raw | blame | history
src/Symbols/SymbolContainer.hpp 132 ●●●● patch | view | raw | blame | history
src/Symbols/SymbolKind.hpp 15 ●●●●● patch | view | raw | blame | history
src/Symbols/SymbolTable.hpp 27 ●●●● patch | view | raw | blame | history
src/Symbols/Value.hpp 103 ●●●●● patch | view | raw | blame | history
src/VoidScript.hpp 82 ●●●● patch | view | raw | blame | history
.clangd
@@ -1,5 +1,5 @@
CompileFlags:
  Add: [-Wunused]
  Add: [-xc++, -Wall]
Index:
  StandardLibrary: No
cli/main.cpp
@@ -1,5 +1,4 @@
#include <filesystem>
#include <fstream>
#include <iostream>
#include <unordered_map>
@@ -58,6 +57,4 @@
    VoidScript voidscript(filename);
    return voidscript.run();
    return 0;
}
src/!NameSpaceManager.hpp
New file
@@ -0,0 +1,144 @@
#ifndef NAMESPACE_MANAGER_H
#define NAMESPACE_MANAGER_H
#include <functional>
#include <map>
#include <memory>
#include <sstream>
#include <string>
#include <vector>
class Namespace {
  public:
    Namespace(const std::string & name = "", Namespace * parent = nullptr) : name_(name), parent_(parent) {}
    /**
    * @brief Add a child namespace to the current namespace.
    * @param name Name of the child namespace.
    */
    Namespace * addChild(const std::string & name) {
        auto it = children_.find(name);
        if (it == children_.end()) {
            children_[name] = std::make_unique<Namespace>(name, this);
        }
        return children_[name].get();
    }
    /**
    * @brief Get the child namespace by name.
    * @param name Name of the child namespace.
    * @return The child namespace
    */
    Namespace * getChild(const std::string & name) const {
        auto it = children_.find(name);
        return it != children_.end() ? it->second.get() : nullptr;
    }
    /**
    * @brief Get the child namespace by name.
    * @param name Name of the child namespace.
    * @return The child namespace
    */
    Namespace * getOrCreate(const std::string & fullName) {
        auto        parts   = split(fullName, '.');
        Namespace * current = this;
        for (const auto & part : parts) {
            current = current->addChild(part);
        }
        return current;
    }
    /**
    * @brief Get the parent namespace.
    * @return The parent namespace.
    */
    Namespace * getParent() const { return parent_; }
    std::string toString() const {
        if (!parent_ || name_.empty()) {
            return name_;
        }
        return parent_->toString() + "." + name_;
    }
    void traverse(const std::function<void(const Namespace &)>& visitor) const {
        visitor(*this);
        for (const auto & [_, child] : children_) {
            child->traverse(visitor);
        }
    }
    const std::string & getName() const { return name_; }
  private:
    std::string                                       name_;
    Namespace *                                       parent_;
    std::map<std::string, std::unique_ptr<Namespace>> children_;
    static std::vector<std::string> split(const std::string & str, char delimiter) {
        std::stringstream        ss(str);
        std::string              part;
        std::vector<std::string> result;
        while (std::getline(ss, part, delimiter)) {
            result.push_back(part);
        }
        return result;
    }
};
class NamespaceManager {
  public:
    static NamespaceManager & instance() {
        static NamespaceManager instance_;
        return instance_;
    }
    /**
     * @brief Constructor for NamespaceManager.
     * @param name Name of the namespace.
     * @param parent Parent namespace.
     */
    NamespaceManager() : root_(""), currentNamespace_(&root_) {}
    /**
    * @brief Get or create a namespace by full name.
    * @param fullName Full name of the namespace.
    * @return The namespace object.
    */
    Namespace * getOrCreate(const std::string & fullName) { return root_.getOrCreate(fullName); }
    /**
    * @brief Set the current namespace.
    * @param fullName Full name of the namespace.
    */
    void setCurrent(const std::string & fullName) {
        Namespace * ns = root_.getOrCreate(fullName);
        if (ns) {
            currentNamespace_ = ns;
        } else {
            currentNamespace_ = &root_;  // fallback
        }
    }
    /**
    * @brief Get the current namespace.
    * @return The current namespace object.
    */
    Namespace * getCurrent() const { return currentNamespace_; }
    /**
    * @brief Reset the current namespace.
    */
    void resetCurrent() { currentNamespace_ = &root_; }
    /**
    * @brief Traverse the namespace tree.
    * @param visitor A function to visit each namespace.
    */
    void traverse(const std::function<void(const Namespace &)>& visitor) const { root_.traverse(visitor); }
  private:
    Namespace   root_;
    Namespace * currentNamespace_;
};
#endif  // NAMESPACE_MANAGER_H
src/Interpreter/BinaryExpressionNode.hpp
New file
@@ -0,0 +1,68 @@
#include <memory>
#include <string>
#include "ExpressionNode.hpp"
namespace Interpreter {
class BinaryExpressionNode : public ExpressionNode {
    std::unique_ptr<ExpressionNode> lhs_;
    std::unique_ptr<ExpressionNode> rhs_;
    std::string                     op_;
  public:
    BinaryExpressionNode(std::unique_ptr<ExpressionNode> lhs, std::string op, std::unique_ptr<ExpressionNode> rhs) :
        lhs_(std::move(lhs)),
        rhs_(std::move(rhs)),
        op_(std::move(op)) {}
    Symbols::Value evaluate(Interpreter & interpreter) const override {
        auto leftVal  = lhs_->evaluate(interpreter);
        auto rightVal = rhs_->evaluate(interpreter);
        if (leftVal.getType() != rightVal.getType()) {
            throw std::runtime_error(
                "Unsupported types in binary expression: " + Symbols::Variables::TypeToString(leftVal.getType()) +
                " and " + Symbols::Variables::TypeToString(rightVal.getType()) + " " + toString());
        }
        if (leftVal.getType() == Symbols::Variables::Type::INTEGER &&
            rightVal.getType() == Symbols::Variables::Type::INTEGER) {
            int l = leftVal.get<int>();
            int r = rightVal.get<int>();
            if (op_ == "+") {
                return Symbols::Value(l + r);
            }
            if (op_ == "-") {
                return Symbols::Value(l - r);
            }
            if (op_ == "*") {
                return Symbols::Value(l * r);
            }
            if (op_ == "/") {
                return Symbols::Value(l / r);  // TODO: 0 div
            }
            throw std::runtime_error("Unknown operator: " + op_);
        }
        if (leftVal.getType() == Symbols::Variables::Type::STRING &&
            rightVal.getType() == Symbols::Variables::Type::STRING) {
            auto l = leftVal.get<std::string>();
            auto r = rightVal.get<std::string>();
            if (op_ == "+") {
                return Symbols::Value(l + r);
            }
            throw std::runtime_error("Unknown operator: " + op_);
        }
        throw std::runtime_error(
            "Unsupported types in binary expression: " + Symbols::Variables::TypeToString(leftVal.getType()) + " and " +
            Symbols::Variables::TypeToString(rightVal.getType()) + " " + toString());
    }
    std::string toString() const override { return "(" + lhs_->toString() + " " + op_ + " " + rhs_->toString() + ")"; }
};
};  // namespace Interpreter
src/Interpreter/DeclareFunctionStatementNode.hpp
New file
@@ -0,0 +1,55 @@
#ifndef INTERPRETER_DEFINE_FUNCTION_STATEMENT_NODE_HPP
#define INTERPRETER_DEFINE_FUNCTION_STATEMENT_NODE_HPP
#include <memory>
#include <string>
#include <utility>
#include "ExpressionNode.hpp"
#include "Interpreter.hpp"
#include "Interpreter/StatementNode.hpp"
#include "Symbols/ParameterContainer.hpp"
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/SymbolFactory.hpp"
namespace Interpreter {
class DeclareFunctionStatementNode : public StatementNode {
    std::string                     functionName_;
    Symbols::Variables::Type        returnType_;
    Symbols::FunctionParameterInfo  params_;
    std::unique_ptr<ExpressionNode> expression_;
    std::string                     ns;
  public:
    DeclareFunctionStatementNode(const std::string & function_name, const std::string & ns,
                                 const Symbols::FunctionParameterInfo & params, Symbols::Variables::Type return_type,
                                 std::unique_ptr<ExpressionNode> expr, const std::string & file_name, int file_line,
                                 size_t line_column) :
        StatementNode(file_name, file_line, line_column),
        functionName_(function_name),
        returnType_(return_type),
        params_(params),
        expression_(std::move(expr)),
        ns(ns) {}
    void interpret(Interpreter & /*interpreter*/) const override {
        //Symbols::Value value = expression_->evaluate(interpreter);
        if (Symbols::SymbolContainer::instance()->exists(functionName_)) {
            throw std::runtime_error("Function already declared: " + functionName_ + " file: " + filename_ +
                                     ", line: " + std::to_string(line_) + ", column: " + std::to_string(column_));
        }
        const auto func = Symbols::SymbolFactory::createFunction(functionName_, ns, params_, "", returnType_);
        Symbols::SymbolContainer::instance()->add(func);
    }
    std::string toString() const override {
        return std::string( " FunctioName: " + functionName_ + " return type: " + Symbols::Variables::TypeToString(returnType_) +
               " params size: " + std::to_string(params_.size()));
    }
};
}  // namespace Interpreter
#endif  // INTERPRETER_DEFINE_FUNCTION_STATEMENT_NODE_HPP
src/Interpreter/DeclareVariableStatementNode.hpp
New file
@@ -0,0 +1,52 @@
#ifndef INTERPRETER_DEFINE_VARIABLE_STATEMENT_NODE_HPP
#define INTERPRETER_DEFINE_VARIABLE_STATEMENT_NODE_HPP
#include <memory>
#include <string>
#include <utility>
#include "ExpressionNode.hpp"
#include "Interpreter.hpp"
#include "Interpreter/StatementNode.hpp"
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/SymbolFactory.hpp"
namespace Interpreter {
class DeclareVariableStatementNode : public StatementNode {
    std::string                     variableName_;
    Symbols::Variables::Type        variableType_;
    std::unique_ptr<ExpressionNode> expression_;
    std::string                     ns;
  public:
    DeclareVariableStatementNode(std::string name, const std::string & ns, Symbols::Variables::Type type,
                                 std::unique_ptr<ExpressionNode> expr, const std::string & file_name, int file_line,
                                 size_t line_column) :
        StatementNode(file_name, file_line, line_column),
        variableName_(std::move(name)),
        variableType_(type),
        expression_(std::move(expr)),
        ns(ns) {}
    void interpret(Interpreter & interpreter) const override {
        Symbols::Value value = expression_->evaluate(interpreter);
        if (Symbols::SymbolContainer::instance()->exists(variableName_)) {
            throw std::runtime_error("Variable already declared: " + variableName_ + " File: " + filename_ +
                                     ", Line: " + std::to_string(line_) + ", Column: " + std::to_string(column_));
        }
        const auto variable = Symbols::SymbolFactory::createVariable(variableName_, value, ns, variableType_);
        Symbols::SymbolContainer::instance()->add(variable);
    }
    std::string toString() const override {
        return std::string("variable name: " + variableName_ +
                           " type: " + Symbols::Variables::TypeToString(variableType_));
    }
};
}  // namespace Interpreter
#endif  // INTERPRETER_DEFINE_VARIABLE_STATEMENT_NODE_HPP
src/Interpreter/ExpressionBuilder.hpp
New file
@@ -0,0 +1,42 @@
#ifndef PARSEREXPRESSION_BUILDER_HPP
#define PARSEREXPRESSION_BUILDER_HPP
#include <memory>
#include <stdexcept>
#include "Interpreter/BinaryExpressionNode.hpp"
#include "Interpreter/ExpressionNode.hpp"
#include "Interpreter/IdentifierExpressionNode.hpp"
#include "Interpreter/LiteralExpressionNode.hpp"
#include "Interpreter/UnaryExpressionNode.hpp" // <-- új include
#include "Parser/ParsedExpression.hpp"
namespace Parser {
static std::unique_ptr<Interpreter::ExpressionNode> buildExpressionFromParsed(
    const Parser::ParsedExpressionPtr & expr) {
    using Kind = Parser::ParsedExpression::Kind;
    switch (expr->kind) {
        case Kind::Literal:
            return std::make_unique<Interpreter::LiteralExpressionNode>(expr->value);
        case Kind::Variable:
            return std::make_unique<Interpreter::IdentifierExpressionNode>(expr->name);
        case Kind::Binary: {
            auto lhs = buildExpressionFromParsed(expr->lhs);
            auto rhs = buildExpressionFromParsed(expr->rhs);
            return std::make_unique<Interpreter::BinaryExpressionNode>(std::move(lhs), expr->op, std::move(rhs));
        }
        case Kind::Unary: {
            auto operand = buildExpressionFromParsed(expr->rhs);  // rhs az operandus
            return std::make_unique<Interpreter::UnaryExpressionNode>(expr->op, std::move(operand));
        }
    }
    throw std::runtime_error("Unknown ParsedExpression kind");
}
}  // namespace Parser
#endif  // PARSEREXPRESSION_BUILDER_HPP
src/Interpreter/ExpressionNode.hpp
@@ -1,16 +1,13 @@
#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;
};
#include "Symbols/Value.hpp"
// Kifejezés (csak int literál most)
namespace Interpreter {
struct ExpressionNode {
    virtual ~ExpressionNode()                                   = default;
    virtual int evaluate(class Interpreter & interpreter) const = 0;
    virtual Symbols::Value evaluate(class Interpreter & interpreter) const = 0;
    virtual std::string    toString() const                                = 0;
};
}  // namespace Interpreter
src/Interpreter/IdentifierExpressionNode.hpp
New file
@@ -0,0 +1,29 @@
#ifndef IDENTIFIER_EXPRESSION_NODE_HPP
#define IDENTIFIER_EXPRESSION_NODE_HPP
#include "ExpressionNode.hpp"
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/Value.hpp"
namespace Interpreter {
class IdentifierExpressionNode : public ExpressionNode {
    std::string name_;
  public:
    explicit IdentifierExpressionNode(std::string name) : name_(std::move(name)) {}
    Symbols::Value evaluate(Interpreter & /*interpreter*/) const override {
        const auto ns = Symbols::SymbolContainer::instance()->currentScopeName() + ".variables";
        if (Symbols::SymbolContainer::instance()->exists(name_, ns)) {
            return Symbols::SymbolContainer::instance()->get(ns, name_)->getValue();
        }
        throw std::runtime_error("Variable " + name_ + " does not exist in ns: " + ns);
    }
    std::string toString() const override { return name_; }
};
}  // namespace Interpreter
#endif  // IDENTIFIER_EXPRESSION_NODE_HPP
src/Interpreter/Interpreter.hpp
@@ -1,46 +1,66 @@
#ifndef INTERPRETER_HPP
#define INTERPRETER_HPP
#include <iostream>
#include <memory>
#include <utility>
#include <vector>
#include <stdexcept>
#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 runOperation(const Operations::Operation & op) {
        std::cout << "Operation: " << op.toString() << "\n";
    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:
            case Operations::Type::Declaration:
                if (op.statement) {
                    op.statement->interpret(*this);
                }
                break;
            case Operations::Type::FuncDeclaration:
                    {
                        int value = op.expression->evaluate(*this);
                        setVariable(op.targetVariable, value);
                    op.statement->interpret(*this);
                }
                break;
            case Operations::Type::Assignment:
                {
                    op.statement->interpret(*this);
                        break;
                    }
                case OperationType::Expression:
            case Operations::Type::Expression:
                    {
                        op.expression->evaluate(*this);  // csak side effect miatt
                    op.statement->interpret(*this);  // csak side effect miatt
                        break;
                    }
            case Operations::Type::FunctionCall:
            case Operations::Type::Return:
            case Operations::Type::Loop:
            case Operations::Type::Break:
            case Operations::Type::Continue:
            case Operations::Type::Block:
            case Operations::Type::Import:
            case Operations::Type::Error:
            case Operations::Type::Conditional:
                // TODO: implementálható később
                break;
            default:
                throw std::runtime_error("Not implemented operation type");
            }
        }
  public:
    Interpreter() {}
    void run() {
        for (const auto & operation : Operations::Container::instance()->getAll()) {
            runOperation(*operation);
        }
    }
};  // class Interpreter
src/Interpreter/LiteralExpressionNode.hpp
New file
@@ -0,0 +1,25 @@
#ifndef LITERAL_EXPRESSION_NODE_HPP
#define LITERAL_EXPRESSION_NODE_HPP
#include "ExpressionNode.hpp"
#include "Symbols/Value.hpp"
namespace Interpreter {
class LiteralExpressionNode : public ExpressionNode {
    Symbols::Value value_;
  public:
    explicit LiteralExpressionNode(Symbols::Value value) : value_(std::move(value)) {}
    Symbols::Value evaluate(class Interpreter & /*interpreter*/) const override { return value_; }
    const Symbols::Value & value() const { return value_; }
    // to string
    std::string toString() const override { return Symbols::Value::to_string(value_); }
};
}  // namespace Interpreter
#endif  // LITERAL_EXPRESSION_NODE_HPP
src/Interpreter/Operation.hpp
@@ -2,30 +2,68 @@
#define INTERPRETER_OPERATION_HPP
#include <cstdint>
#include <memory>
#include <string>
#include <unordered_map>
#include "ExpressionNode.hpp"
#include "Interpreter/StatementNode.hpp"
namespace Interpreter {
enum OperationType : std::uint8_t {
    Assignment,
    Expression,
namespace Operations {
enum class Type : std::uint8_t {
    Assignment,       // Variable assignment, e.g., $x = 5
    Expression,       // Evaluation of an expression (can be evaluated without side effects)
    FunctionCall,     // Call to a function, e.g., print(x)
    FuncDeclaration,  // declaration of new function
    Return,           // return statement
    Conditional,      // if/else structure
    Loop,             // while/for loop
    Break,            // break out of a loop
    Continue,         // continue with the next iteration of a loop
    Block,            // block of statements, e.g. { ... }
    Declaration,      // declaration of new variable (if different from assignment) int $x = 1
    Import,           // import of another script or module
    Error,            // error or non-interpretable operation (error handling)
};
struct Operation {
    OperationType type;
    Operation() = default;
    Operations::Type type;
    // Általános mezők
    std::string targetVariable;
    std::unique_ptr<ExpressionNode> expression;
    std::string                                 targetName;
    std::unique_ptr<Interpreter::StatementNode> statement;
    Operation(OperationType t, std::string var, std::unique_ptr<ExpressionNode> expr)
        : type(t), targetVariable(std::move(var)), expression(std::move(expr)) {}
    Operation(Operations::Type t, std::string target_variable, std::unique_ptr<Interpreter::StatementNode> stmt) :
        type(t),
        targetName(std::move(target_variable)),
        statement(std::move(stmt)) {}
    std::string typeToString() const {
        std::unordered_map<Operations::Type, std::string> types = {
            { Operations::Type::Assignment,      "Assignment"      },
            { Operations::Type::Expression,      "Expression"      },
            { Operations::Type::FunctionCall,    "FunctionCall"    },
            { Operations::Type::FuncDeclaration, "FuncDeclaration" },
            { Operations::Type::Return,          "Return"          },
            { Operations::Type::Conditional,     "Conditional"     },
            { Operations::Type::Loop,            "Loop"            },
            { Operations::Type::Break,           "Break"           },
            { Operations::Type::Continue,        "Continue"        },
            { Operations::Type::Block,           "Block"           },
            { Operations::Type::Declaration,     "Declaration"     },
            { Operations::Type::Import,          "Import"          },
            { Operations::Type::Error,           "Error"           }
};
        auto it = types.find(type);
        if (it != types.end()) {
            return it->second;
        }
        return "Unknown type";
    }
};  // namespace Interpreter
    std::string toString() const {
        return "Target: " + targetName + " Type: " + this->typeToString() + " Statement: " + statement->toString();
     }
};
};  // namespace Operations
#endif
src/Interpreter/OperationContainer.hpp
@@ -1,12 +1,135 @@
#ifndef INTERPRETER_OPERATION_CONTAINER_HPP
#define INTERPRETER_OPERATION_CONTAINER_HPP
#include <map>
#include <string>
#include <vector>
#include "Interpreter/Operation.hpp"
namespace Interpreter {
using OperationContainer = std::vector<Operation>;
};  // namespace Interpreter
namespace Operations {
class Container {
  public:
    /**
     * @brief Get the Operations::Container instance.
     * @return The Operations::Container instance.
     */
    static Operations::Container * instance() {
        static Operations::Container instance_;
        return &instance_;
    }
    Container() = default;
    void add(const std::string & ns, Operations::Operation operation) {
        this->_operations[ns].emplace_back(std::make_shared<Operations::Operation>(std::move(operation)));
    }
    /**
    * @brief Returns the first operation in the namespace.
    * @param ns Namespace from which to get the operation.
    * @return The first operation in the namespace.
    */
    std::shared_ptr<Operations::Operation> getFirst(const std::string & ns) {
        auto it = _operations.find(ns);
        if (it != _operations.end()) {
            return it->second.front();
        }
        return nullptr;
    }
    /**
     * @brief Removes the first operation from the namespace.
     * @param ns Namespace from which to remove the operation.
     * @return The removed operation.
     */
    std::shared_ptr<Operations::Operation> pullFirst(const std::string & ns) {
        auto it = _operations.find(ns);
        if (it != _operations.end()) {
            auto operation = it->second.front();
            it->second.erase(it->second.begin());
            return operation;
        }
        return nullptr;
    }
    /**
     * @brief Removes the last operation from the namespace.
     * @param ns Namespace from which to remove the operation.
     * @return The removed operation.
     */
    std::shared_ptr<Operations::Operation> pullLast(const std::string & ns) {
        auto it = _operations.find(ns);
        if (it != _operations.end()) {
            auto operation = it->second.back();
            it->second.pop_back();
            return operation;
        }
        return nullptr;
    }
    /**
    * @brief Returns the last operation in the namespace.
    * @param ns Namespace from which to get the operation.
    * @return The last operation in the namespace.
    */
    std::shared_ptr<Operations::Operation> getLast(const std::string & ns) {
        auto it = _operations.find(ns);
        if (it != _operations.end()) {
            return it->second.back();
        }
        return nullptr;
    }
    /**
     * @brief Returns all operations in the namespace.
     * @param ns Namespace from which to get the operations.
     * @return All operations in the namespace.
     */
    std::vector<std::shared_ptr<Operations::Operation>> getAll(const std::string & ns) {
        auto it = _operations.find(ns);
        if (it != _operations.end()) {
            return it->second;
        }
        return {};
    }
    /**
     * @brief Returns all operations from all namespaces
     * @return All operations in the namespace.
     */
    std::vector<std::shared_ptr<Operations::Operation>> getAll() {
        std::vector<std::shared_ptr<Operations::Operation>> result;
        for (const auto & [_, table] : _operations) {
            result.insert(result.end(), table.begin(), table.end());
        }
        return result;
    }
    auto begin() { return _operations.begin(); }
    auto end() { return _operations.end(); }
    auto begin() const { return _operations.begin(); }
    auto end() const { return _operations.end(); }
    static std::string dump()  {
        std::string result = "";
        for (const auto & [_, table] : Operations::Container::instance()->_operations) {
            result += "Namespace: " + _ + "\n";
            for (const auto & operation : table) {
                result += "  Operation: " + operation->toString() + "\n";
            }
        }
        return result;
    }
  private:
    std::map<std::string, std::vector<std::shared_ptr<Operations::Operation>>> _operations;
};  // class Container
};  // namespace Operations
#endif  // INTERPRETER_OPERATION_CONTAINER_HPP
src/Interpreter/OperationsFactory.hpp
New file
@@ -0,0 +1,59 @@
#ifndef OPERATIONSFACTORY_HPP
#define OPERATIONSFACTORY_HPP
#include <memory>
#include <string>
#include "Interpreter/DeclareFunctionStatementNode.hpp"
#include "Interpreter/DeclareVariableStatementNode.hpp"
#include "Interpreter/ExpressionBuilder.hpp"
#include "Interpreter/LiteralExpressionNode.hpp"
#include "Interpreter/Operation.hpp"
#include "Interpreter/OperationContainer.hpp"
#include "Parser/ParsedExpression.hpp"
#include "Symbols/ParameterContainer.hpp"
#include "Symbols/Value.hpp"
namespace Interpreter {
class OperationsFactory {
  public:
    OperationsFactory() {}
    static void defineFunction(const std::string & functionName, const Symbols::FunctionParameterInfo & params,
                               const Symbols::Variables::Type & returnType, const std::string & ns,
                               const std::string & fileName, int line, size_t column) {
        std::unique_ptr<DeclareFunctionStatementNode> stmt = std::make_unique<DeclareFunctionStatementNode>(
            functionName, ns, params, returnType, nullptr, fileName, line, column);
        Operations::Container::instance()->add(
            ns, Operations::Operation{ Operations::Type::FuncDeclaration, functionName, std::move(stmt) });
    }
    static void defineSimpleVariable(const std::string & varName, const Symbols::Value & value, const std::string & ns,
                                     const std::string & filename, int line, size_t column) {
        auto literalExpr = std::make_unique<LiteralExpressionNode>(value);
        std::unique_ptr<DeclareVariableStatementNode> stmt = std::make_unique<DeclareVariableStatementNode>(
            varName, ns, value.getType(), std::move(literalExpr), filename, line, column);
        Operations::Container::instance()->add(
            ns, Operations::Operation{ Operations::Type::Declaration, varName, std::move(stmt) });
    }
    static void defineVariableWithExpression(const std::string & varName, Symbols::Variables::Type type,
                                             const Parser::ParsedExpressionPtr pexpr, const std::string & ns,
                                             const std::string & filename, int line, size_t column) {
                                              // ParsedExpression → ExpressionNode
                                              std::unique_ptr<ExpressionNode> expr = buildExpressionFromParsed(pexpr);
                                              std::unique_ptr<DeclareVariableStatementNode> stmt = std::make_unique<DeclareVariableStatementNode>(
                                                  varName, ns, type, std::move(expr), filename, line, column);
                                              Operations::Container::instance()->add(
                                                  ns, Operations::Operation{Operations::Type::Declaration, varName, std::move(stmt)});
                                          }
};
}  // namespace Interpreter
#endif  // OPERATIONSFACTORY_HPP
src/Interpreter/StatementNode.hpp
New file
@@ -0,0 +1,26 @@
#ifndef STATEMENT_NODE_HPP
#define STATEMENT_NODE_HPP
#include <string>
namespace Interpreter {
class StatementNode {
  public:
    std::string filename_;
    int         line_;
    size_t      column_;
    StatementNode(const std::string & file_name, int file_line, size_t line_column) :
        filename_(file_name),
        line_(file_line),
        column_(line_column) {}
    virtual ~StatementNode()                                      = default;
    virtual void interpret(class Interpreter & interpreter) const = 0;
    virtual std::string toString() const = 0;
};
};  // namespace Interpreter
#endif  // STATEMENT_NODE_HPP
src/Interpreter/UnaryExpressionNode.hpp
New file
@@ -0,0 +1,64 @@
#ifndef UNARY_EXPRESSION_NODE_HPP
#define UNARY_EXPRESSION_NODE_HPP
#include <memory>
#include <string>
#include "ExpressionNode.hpp"
namespace Interpreter {
class UnaryExpressionNode : public ExpressionNode {
    std::string                     op_;
    std::unique_ptr<ExpressionNode> operand_;
  public:
    UnaryExpressionNode(std::string op, std::unique_ptr<ExpressionNode> operand) :
        op_(std::move(op)),
        operand_(std::move(operand)) {}
    Symbols::Value evaluate(Interpreter & interpreter) const override {
        auto value = operand_->evaluate(interpreter);
        auto type  = value.getType();
        if (type == Symbols::Variables::Type::INTEGER) {
            int v = value.get<int>();
            if (op_ == "-") {
                return Symbols::Value(-v);
            }
            if (op_ == "+") {
                return Symbols::Value(+v);
            }
        } else if (type == Symbols::Variables::Type::DOUBLE) {
            double v = value.get<double>();
            if (op_ == "-") {
                return Symbols::Value(-v);
            }
            if (op_ == "+") {
                return Symbols::Value(+v);
            }
        } else if (type == Symbols::Variables::Type::FLOAT) {
            float v = value.get<float>();
            if (op_ == "-") {
                return Symbols::Value(-v);
            }
            if (op_ == "+") {
                return Symbols::Value(+v);
            }
        } else if (type == Symbols::Variables::Type::BOOLEAN) {
            bool v = value.get<bool>();
            if (op_ == "!") {
                return Symbols::Value(!v);
            }
        }
        throw std::runtime_error("Unsupported unary operator '" + op_ +
                                 "' for type: " + Symbols::Variables::TypeToString(type));
    }
    std::string toString() const override { return "(" + op_ + operand_->toString() + ")"; }
};
}  // namespace Interpreter
#endif  // UNARY_EXPRESSION_NODE_HPP
src/Interpreter/VariableExpressionNode.hpp
New file
@@ -0,0 +1,36 @@
#ifndef VARIABLEEXPRESSIONNODE_HPP
#define VARIABLEEXPRESSIONNODE_HPP
#include <memory>
#include <string>
#include <utility>
#include "Interpreter/ExpressionNode.hpp"
#include "Interpreter/Interpreter.hpp"
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/Value.hpp"
namespace Interpreter {
class VariableExpressionNode : public ExpressionNode {
    std::string variableName_;
    std::string ns;
  public:
    VariableExpressionNode(std::string varName, std::string ns) :
        variableName_(std::move(varName)),
        ns(std::move(ns)) {}
    Symbols::Value evaluate(Interpreter & interpreter) const override {
        if (!Symbols::SymbolContainer::instance()->exists(variableName_)) {
            throw std::runtime_error("Variable not found: " + variableName_);
        }
        auto symbol = Symbols::SymbolContainer::instance()->get(ns, variableName_);
        return symbol->getValue();
    }
    std::string toString() const override { return "$" + variableName_; }
};
};  // namespace Interpreter
#endif  // VARIABLEEXPRESSIONNODE_HPP
src/Interpreter/VariableReferenceNode.hpp
New file
@@ -0,0 +1,37 @@
#ifndef INTERPRETER_VARIABLE_REFERENCE_NODE_HPP
#define INTERPRETER_VARIABLE_REFERENCE_NODE_HPP
#include <memory>
#include <stdexcept>
#include <string>
#include "ExpressionNode.hpp"
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/Value.hpp"
#include "Symbols/VariableSymbol.hpp"
namespace Interpreter {
class VariableReferenceNode : public ExpressionNode {
    std::string variableName_;
    std::string namespace_;  // pl. "global.variables"
  public:
    VariableReferenceNode(const std::string & variableName, const std::string & ns) :
        variableName_(variableName),
        namespace_(ns) {}
    Symbols::Value evaluate(class Interpreter & /*interpreter*/) const override {
        auto symbol = Symbols::SymbolContainer::instance()->get(namespace_, variableName_);
        if (!symbol) {
            throw std::runtime_error("Variable not found: " + variableName_);
        }
        auto varSymbol = std::dynamic_pointer_cast<Symbols::VariableSymbol>(symbol);
        if (!varSymbol) {
            throw std::runtime_error("Symbol is not a variable: " + variableName_);
        }
        return varSymbol->getValue();
    }
};
};  // namespace Interpreter
#endif  // INTERPRETER_VARIABLE_REFERENCE_NODE_HPP
src/Lexer/Lexer.hpp
@@ -1,14 +1,15 @@
#ifndef LEXER_HPP
#define LEXER_HPP
#include <algorithm>    // std::find_if
#include <cctype>       // <<< Hozzáadva
#include <algorithm>
#include <cctype>
#include <string>
#include <string_view>  // <<< Hozzáadva
#include <string_view>
#include <unordered_map>
#include <vector>
#include "Token.hpp"  // Feltételezzük, hogy ez a fenti Token.hpp
#include "Symbols/SymbolContainer.hpp"
#include "Token.hpp"
namespace Lexer {
class Lexer {
@@ -32,14 +33,13 @@
        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);
        Symbols::SymbolContainer::instance()->enter(ns);
        std::vector<Tokens::Token> tokens;
        Tokens::Token              token;
        do {
@@ -96,7 +96,6 @@
    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
@@ -109,13 +108,41 @@
    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_); }
    const std::string & input() const {
        const auto & ns = Symbols::SymbolContainer::instance()->currentScopeName();
        auto         it = inputs_.find(ns);
        if (it != inputs_.end()) {
            return it->second;
        }
        throw std::runtime_error("Input not found in namespace: " + ns);
    }
    size_t & pos() { return positions_[current_namespace_]; }
    size_t & pos() {
        const auto & ns = Symbols::SymbolContainer::instance()->currentScopeName();
        auto         it = positions_.find(ns);
        if (it != positions_.end()) {
            return it->second;
        }
        throw std::runtime_error("Unknown position in namespace: " + ns);
    }
    int & line() { return line_numbers_[current_namespace_]; }
    int & line() {
        const auto & ns = Symbols::SymbolContainer::instance()->currentScopeName();
        auto         it = line_numbers_.find(ns);
        if (it != line_numbers_.end()) {
            return it->second;
        }
        throw std::runtime_error("Unknown line number in namespace: " + ns);
    }
    int & col() { return column_numbers_[current_namespace_]; }
    int & col() {
        const auto & ns = Symbols::SymbolContainer::instance()->currentScopeName();
        auto         it = column_numbers_.find(ns);
        if (it != column_numbers_.end()) {
            return it->second;
        }
        throw std::runtime_error("Unknown column number in namespace: " + ns);
    }
    Tokens::Token createToken(Tokens::Type type, size_t start, size_t end, const std::string & value = "") {
        Tokens::Token token;
@@ -134,8 +161,9 @@
    // --------------------------------------
    char peek(size_t offset = 0) const {
        const auto & in = inputs_.at(current_namespace_);
        size_t       cp = positions_.at(current_namespace_);
        const auto & ns = Symbols::SymbolContainer::instance()->currentScopeName();
        const auto & in = inputs_.at(ns);
        size_t       cp = positions_.at(ns);
        if (cp + offset >= in.length()) {
            return '\0';
        }
@@ -154,10 +182,9 @@
        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 == '#');
    bool isAtEnd() const {
        const auto & ns = Symbols::SymbolContainer::instance()->currentScopeName();
        return positions_.at(ns) >= inputs_.at(ns).length();
    }
    void skipWhitespaceAndComments() {
src/Lexer/TokenType.hpp
@@ -25,7 +25,7 @@
    KEYWORD_DOUBLE,
    KEYWORD_FLOAT,
    KEYWORD_BOOLEAN,
    KEYWORD_FUNCTION,
    KEYWORD_FUNCTION_DECLARATION,
    KEYWORD_RETURN,
    KEYWORD_NULL,
    UNKNOWN  // Ismeretlen karakter/szekvencia
@@ -69,7 +69,7 @@
            return "KEYWORD_FLOAT";
        case Lexer::Tokens::Type::KEYWORD_BOOLEAN:
            return "KEYWORD_BOOLEAN";
        case Lexer::Tokens::Type::KEYWORD_FUNCTION:
        case Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION:
            return "KEYWORD_FUNCTION";
        case Lexer::Tokens::Type::KEYWORD_RETURN:
            return "KEYWORD_RETURN";
src/Parser/ParsedExpression.hpp
New file
@@ -0,0 +1,66 @@
#ifndef PARSEDEXPRESSION_HPP
#define PARSEDEXPRESSION_HPP
#include <memory>
#include <string>
#include "Symbols/Value.hpp"
namespace Parser {
struct ParsedExpression;
using ParsedExpressionPtr = std::unique_ptr<ParsedExpression>;
struct ParsedExpression {
    enum class Kind : std::uint8_t { Literal, Variable, Binary, Unary };
    Kind kind;
    Symbols::Value value;
    std::string    name;
    // Műveletekhez
    std::string         op;
    ParsedExpressionPtr lhs;
    ParsedExpressionPtr rhs;
    // Konstruktor literálhoz
    static ParsedExpressionPtr makeLiteral(const Symbols::Value & val) {
        auto expr   = std::make_unique<ParsedExpression>();
        expr->kind  = Kind::Literal;
        expr->value = val;
        return expr;
    }
    // Konstruktor változóhoz
    static ParsedExpressionPtr makeVariable(const std::string & name) {
        auto expr  = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Variable;
        expr->name = name;
        return expr;
    }
    // Konstruktor binárishoz
    static ParsedExpressionPtr makeBinary(std::string op, ParsedExpressionPtr left, ParsedExpressionPtr right) {
        auto expr  = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Binary;
        expr->op   = std::move(op);
        expr->lhs  = std::move(left);
        expr->rhs  = std::move(right);
        return expr;
    }
    // Konstruktor unárishoz
    static ParsedExpressionPtr makeUnary(std::string op, ParsedExpressionPtr operand) {
        auto expr  = std::make_unique<ParsedExpression>();
        expr->kind = Kind::Unary;
        expr->op   = std::move(op);
        expr->rhs  = std::move(operand);
        return expr;
    }
};
}  // namespace Parser
#endif  // PARSEDEXPRESSION_HPP
src/Parser/Parser.cpp
@@ -9,7 +9,7 @@
    { "while",    Lexer::Tokens::Type::KEYWORD          },
    { "for",      Lexer::Tokens::Type::KEYWORD          },
    { "return",   Lexer::Tokens::Type::KEYWORD_RETURN   },
    { "function", Lexer::Tokens::Type::KEYWORD_FUNCTION },
    { "function", Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION },
    // Régebbiek:
    { "const",    Lexer::Tokens::Type::KEYWORD          },
    { "true",     Lexer::Tokens::Type::KEYWORD          },
src/Parser/Parser.hpp
@@ -3,17 +3,19 @@
#include <algorithm>
#include <memory>
#include <sstream>  // Hibaüzenetekhez
#include <stack>
#include <stdexcept>
#include <string>
#include <vector>
// Szükséges header-ök a Lexer és Symbol komponensekből
#include "Interpreter/ExpressionBuilder.hpp"
#include "Interpreter/OperationsFactory.hpp"
#include "Lexer/Token.hpp"
#include "Lexer/TokenType.hpp"  // Enum és TypeToString
#include "Lexer/TokenType.hpp"
#include "Parser/ParsedExpression.hpp"
#include "Symbols/ParameterContainer.hpp"
#include "Symbols/SymbolContainer.hpp"
#include "Symbols/SymbolFactory.hpp"
#include "Symbols/Value.hpp"  // Symbols::Value miatt
#include "Symbols/Value.hpp"
namespace Parser {
@@ -32,11 +34,13 @@
  public:
    Parser() {}
    void parseProgram(const std::vector<Lexer::Tokens::Token> & tokens, std::string_view input_string) {
    void parseScript(const std::vector<Lexer::Tokens::Token> & tokens, std::string_view input_string,
                     const std::string & filename) {
        tokens_              = tokens;
        input_str_view_      = input_string;
        current_token_index_ = 0;
        symbol_container_    = std::make_unique<Symbols::SymbolContainer>();
        current_filename_    = filename;
        try {
            while (!isAtEnd() && currentToken().type != Lexer::Tokens::Type::END_OF_FILE) {
                parseStatement();
@@ -52,13 +56,6 @@
        }
    }
    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;
@@ -66,7 +63,7 @@
    std::vector<Lexer::Tokens::Token>         tokens_;
    std::string_view                          input_str_view_;
    size_t                                    current_token_index_;
    std::shared_ptr<Symbols::SymbolContainer> symbol_container_;
    std::string                       current_filename_;
    // Token Stream Kezelő és Hibakezelő segédfüggvények (változatlanok)
    const Lexer::Tokens::Token & currentToken() const {
@@ -182,7 +179,7 @@
    void parseStatement() {
        const auto & token_type = currentToken().type;
        if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION) {
        if (token_type == Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION) {
            parseFunctionDefinition();
            return;
        }
@@ -197,40 +194,33 @@
        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;
        Symbols::Variables::Type var_type = parseType();
        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);
        }
        const auto ns = Symbols::SymbolContainer::instance()->currentScopeName();
        expect(Lexer::Tokens::Type::OPERATOR_ASSIGNMENT, "=");
        Symbols::Value initial_value = parseValue(var_type_enum);
        /*
        Symbols::Value initial_value = parseValue(var_type);
        Interpreter::OperationsFactory::defineSimpleVariable(var_name, initial_value, ns, this->current_filename_,
                                                             id_token.line_number, id_token.column_number);
*/
        auto expr = parseParsedExpression(var_type);
        Interpreter::OperationsFactory::defineVariableWithExpression(
            var_name, var_type, std::move(expr), ns, current_filename_, id_token.line_number, id_token.column_number);
        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);
        expect(Lexer::Tokens::Type::KEYWORD_FUNCTION_DECLARATION);
        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;
@@ -242,7 +232,6 @@
        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)
@@ -275,50 +264,10 @@
            }
        }
        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";
        parseFunctionBody(opening_brace, func_name, func_return_type, param_infos);
    }
    // --- Elemzési Segédfüggvények ---
@@ -337,118 +286,96 @@
        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;
        // Előjel kezelése
        if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC && (token.lexeme == "-" || token.lexeme == "+") &&
            peekToken().type == Lexer::Tokens::Type::NUMBER) {
            is_negative = (token.lexeme == "-");
            token       = peekToken();
            consumeToken();
            consumeToken();  // előjelet elfogyasztottuk
        }
        // STRING típus
        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
                return Symbols::Value(token.value);
            }
            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;
                    }
        // BOOLEAN típus
        if (expected_var_type == Symbols::Variables::Type::BOOLEAN) {
            if (token.type == Lexer::Tokens::Type::KEYWORD && (token.value == "true" || token.value == "false")) {
                    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);
                return Symbols::Value(token.value == "true");
                }
            reportError("Expected boolean literal value (true or false)");
            }
            reportError("Expected numeric literal value for double");
        } else if (expected_var_type == Symbols::Variables::Type::BOOLEAN) {
            if (token.type == Lexer::Tokens::Type::KEYWORD) {
        // NUMERIC típusok
        if (expected_var_type == Symbols::Variables::Type::INTEGER ||
            expected_var_type == Symbols::Variables::Type::DOUBLE ||
            expected_var_type == Symbols::Variables::Type::FLOAT) {
            if (token.type == Lexer::Tokens::Type::NUMBER) {
                Symbols::Value val = parseNumericLiteral(token.value, is_negative, expected_var_type);
                consumeToken();
                return Symbols::Value(token.value == "true");  // A lexer value-ja már a feldolgozott string
                return val;
            }
            reportError("Expected boolean literal value");
        } else {
            // Más típusok (pl. boolean) itt kezelendők, ha lennének
            reportError("Expected numeric literal value");
        }
            reportError("Unsupported variable type encountered during value parsing");
        }
        // Should not reach here due to reportError throwing
        return Symbols::Value();  // Default return to satisfy compiler
        return Symbols::Value();  // compiler happy
    }
    Symbols::SymbolContainer parseFunctionBody(const Lexer::Tokens::Token & opening_brace,
                                               bool                         return_required = false) {
    Symbols::Value parseNumericLiteral(const std::string & value, bool is_negative, Symbols::Variables::Type type) {
        try {
            switch (type) {
                case Symbols::Variables::Type::INTEGER:
                    {
                        if (value.find('.') != std::string::npos) {
                            throw std::invalid_argument("Floating point value in integer context: " + value);
                        }
                        int v = std::stoi(value);
                        return Symbols::Value(is_negative ? -v : v);
                    }
                case Symbols::Variables::Type::DOUBLE:
                    {
                        double v = std::stod(value);
                        return Symbols::Value(is_negative ? -v : v);
                    }
                case Symbols::Variables::Type::FLOAT:
                    {
                        float v = std::stof(value);
                        return Symbols::Value(is_negative ? -v : v);
                    }
                default:
                    throw std::invalid_argument("Unsupported numeric type");
            }
        } catch (const std::invalid_argument & e) {
            reportError("Invalid numeric literal: " + value + " (" + e.what() + ")");
        } catch (const std::out_of_range & e) {
            reportError("Numeric literal out of range: " + value + " (" + e.what() + ")");
        }
        return Symbols::Value();  // unreachable
    }
    void parseFunctionBody(const Lexer::Tokens::Token & opening_brace, const std::string & function_name,
                           Symbols::Variables::Type return_type, const Symbols::FunctionParameterInfo & params) {
        size_t               braceDepth = 0;
        int                  peek       = 0;
        int                  tokenIndex = current_token_index_;
        Lexer::Tokens::Token currentToken_;
        Lexer::Tokens::Token closing_brace;
        while (tokenIndex < tokens_.size()) {
            currentToken_ = peekToken();
            currentToken_ = peekToken(peek);
            if (currentToken_.type == Lexer::Tokens::Type::PUNCTUATION) {
                if (currentToken_.value == "{") {
                    ++braceDepth;
@@ -460,6 +387,8 @@
                    --braceDepth;
                }
            }
            tokenIndex++;
            peek++;
        }
        if (braceDepth != 0) {
            reportError("Unmatched braces in function body");
@@ -468,65 +397,190 @@
        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();
        current_token_index_ = tokenIndex;
        expect(Lexer::Tokens::Type::PUNCTUATION, "}");
        const std::string newns = Symbols::SymbolContainer::instance()->currentScopeName() + "." + function_name;
        Symbols::SymbolContainer::instance()->create(newns);
        std::shared_ptr<Parser> parser = std::make_shared<Parser>();
        parser->parseScript(filtered_tokens, input_string, this->current_filename_);
        Symbols::SymbolContainer::instance()->enterPreviousScope();
        // create function
        Interpreter::OperationsFactory::defineFunction(
            function_name, params, return_type, Symbols::SymbolContainer::instance()->currentScopeName(),
            this->current_filename_, currentToken_.line_number, currentToken_.column_number);
    }
    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;
    ParsedExpressionPtr parseParsedExpression(const Symbols::Variables::Type & expected_var_type) {
        std::stack<std::string>          operator_stack;
        std::vector<ParsedExpressionPtr> output_queue;
        auto getPrecedence = [](const std::string & op) -> int {
            if (op == "+" || op == "-") {
                return 1;
            }
            if (op == "*" || op == "/") {
                return 2;
            }
            if (op == "u-" || op == "u+") {
                return 3;
            }
            return 0;
        };
        auto isLeftAssociative = [](const std::string & op) -> bool {
            return !(op == "u-" || op == "u+");
        };
        auto applyOperator = [](const std::string & op, ParsedExpressionPtr rhs, ParsedExpressionPtr lhs = nullptr) {
            if (op == "u-" || op == "u+") {
                std::string real_op = (op == "u-") ? "-" : "+";
                return ParsedExpression::makeUnary(real_op, std::move(rhs));
            } else {
                return ParsedExpression::makeBinary(op, std::move(lhs), std::move(rhs));
            }
        };
        auto pushOperand = [&](const Lexer::Tokens::Token & token) {
            if (token.type == Lexer::Tokens::Type::NUMBER || token.type == Lexer::Tokens::Type::STRING_LITERAL ||
                token.type == Lexer::Tokens::Type::KEYWORD) {
                output_queue.push_back(
                    ParsedExpression::makeLiteral(Symbols::Value::fromString(token.value, expected_var_type)));
            } else if (token.type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER) {
                std::string name = token.value;
                if (!name.empty() && name[0] == '$') {
                    name = name.substr(1);
                }
                output_queue.push_back(ParsedExpression::makeVariable(name));
            } else {
                reportError("Expected literal or variable");
            }
        };
        bool expect_unary = true;
        while (true) {
            if (isAtEnd()) {
                reportError("Unexpected end of file inside function body (missing '}')");
            }
            const auto & token = currentToken();
            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);
            if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.lexeme == "(") {
                operator_stack.push("(");
                consumeToken();
                expect_unary = true;
            } else if (token.type == Lexer::Tokens::Type::PUNCTUATION && token.lexeme == ")") {
                consumeToken();
                while (!operator_stack.empty() && operator_stack.top() != "(") {
                    std::string op = operator_stack.top();
                    operator_stack.pop();
                    if (op == "u-" || op == "u+") {
                        if (output_queue.empty()) {
                            reportError("Missing operand for unary operator");
                        }
                        auto rhs = std::move(output_queue.back());
                        output_queue.pop_back();
                        output_queue.push_back(applyOperator(op, std::move(rhs)));
                } else {
                    reportError("Invalid position range in body reconstruction");
                        if (output_queue.size() < 2) {
                            reportError("Malformed expression");
                        }
                        auto rhs = std::move(output_queue.back());
                        output_queue.pop_back();
                        auto lhs = std::move(output_queue.back());
                        output_queue.pop_back();
                        output_queue.push_back(applyOperator(op, std::move(rhs), std::move(lhs)));
                }
            }
            if (token.type == Lexer::Tokens::Type::KEYWORD_RETURN) {
                has_return = true;
                if (operator_stack.empty() || operator_stack.top() != "(") {
                    reportError("Mismatched parentheses");
                }
                operator_stack.pop();  // remove "("
                expect_unary = false;
            } else if (token.type == Lexer::Tokens::Type::OPERATOR_ARITHMETIC) {
                std::string op = std::string(token.lexeme);
                if (expect_unary && (op == "-" || op == "+")) {
                    op = "u" + op;  // pl. u-
            }
            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
                while (!operator_stack.empty()) {
                    const std::string & top = operator_stack.top();
                    if ((isLeftAssociative(op) && getPrecedence(op) <= getPrecedence(top)) ||
                        (!isLeftAssociative(op) && getPrecedence(op) < getPrecedence(top))) {
                        operator_stack.pop();
                        if (top == "u-" || top == "u+") {
                            if (output_queue.empty()) {
                                reportError("Missing operand for unary operator");
                            }
                            auto rhs = std::move(output_queue.back());
                            output_queue.pop_back();
                            output_queue.push_back(applyOperator(top, std::move(rhs)));
                        } else {
                            if (output_queue.size() < 2) {
                                reportError("Malformed expression");
                            }
                            auto rhs = std::move(output_queue.back());
                            output_queue.pop_back();
                            auto lhs = std::move(output_queue.back());
                            output_queue.pop_back();
                            output_queue.push_back(applyOperator(top, std::move(rhs), std::move(lhs)));
                        }
                    } else {
                    break;
                }
                body_ss << token.lexeme;
            } else {
                body_ss << token.lexeme;
            }
            last_token_end_pos = token.end_pos;
                operator_stack.push(op);
            consumeToken();
                expect_unary = true;
            } else if (token.type == Lexer::Tokens::Type::NUMBER || token.type == Lexer::Tokens::Type::STRING_LITERAL ||
                       token.type == Lexer::Tokens::Type::KEYWORD ||
                       token.type == Lexer::Tokens::Type::VARIABLE_IDENTIFIER) {
                pushOperand(token);
                consumeToken();
                expect_unary = false;
            } else {
                break;
            }
        }
        if (return_required && !has_return) {
            return "";
        // Kiürítjük az operator stack-et
        while (!operator_stack.empty()) {
            std::string op = operator_stack.top();
            operator_stack.pop();
            if (op == "(" || op == ")") {
                reportError("Mismatched parentheses");
        }
        return body_ss.str();
            if (op == "u-" || op == "u+") {
                if (output_queue.empty()) {
                    reportError("Missing operand for unary operator");
                }
                auto rhs = std::move(output_queue.back());
                output_queue.pop_back();
                output_queue.push_back(applyOperator(op, std::move(rhs)));
            } else {
                if (output_queue.size() < 2) {
                    reportError("Malformed expression");
                }
                auto rhs = std::move(output_queue.back());
                output_queue.pop_back();
                auto lhs = std::move(output_queue.back());
                output_queue.pop_back();
                output_queue.push_back(applyOperator(op, std::move(rhs), std::move(lhs)));
            }
        }
        if (output_queue.size() != 1) {
            reportError("Expression could not be parsed cleanly");
        }
        return std::move(output_queue.back());
    }
};  // class Parser
src/Symbols/BaseSymbol.hpp
@@ -13,7 +13,7 @@
  protected:
    std::string   name_;
    Value         value_;
    std::string   context_;
    std::string   context_; // ns
    Symbols::Kind kind_;
  public:
@@ -40,6 +40,15 @@
    virtual void setValue(const Value & value) { value_ = value; }
    std::string dump() const {
        std::string r = "\t\t  "+ kindToString(this->kind_) + " name: '" + name_ + "' \n\t\t\tContext: " + context_;
        r += " \n\t\t\tType: " + Symbols::Variables::TypeToString(value_.getType());
        r += " \n\t\t\tValue: '" + Symbols::Value::to_string(value_) + "'";
        return r;
    }
    // Templated getter
    template <typename T> T getAs() const { return std::get<T>(value_); }
};
src/Symbols/SymbolContainer.hpp
@@ -1,49 +1,139 @@
// SymbolContainer.hpp
#ifndef SYMBOL_CONTAINER_HPP
#define SYMBOL_CONTAINER_HPP
#include <memory>
#include <stdexcept>
#include <unordered_map>
#include "SymbolTable.hpp"
#define NSMGR Symbols::SymbolContainer::instance()
namespace Symbols {
class SymbolContainer {
    std::shared_ptr<SymbolTable> globalScope_;
    std::shared_ptr<SymbolTable> currentScope_;
    std::unordered_map<std::string, std::shared_ptr<SymbolTable>> scopes_;
    std::string                                                   currentScope_  = "global";
    std::string                                                   previousScope_ = "global";
public:
    SymbolContainer() {
        globalScope_ = std::make_shared<SymbolTable>();
        currentScope_ = globalScope_;
    static SymbolContainer * instance() {
        static SymbolContainer instance_;
        return &instance_;
    }
    void enterScope() {
        currentScope_ = std::make_shared<SymbolTable>(currentScope_);
    explicit SymbolContainer(const std::string & default_scope_name = "global") { create(default_scope_name); }
    // --- Scope management ---
    void create(const std::string & name) {
        scopes_[name]  = std::make_shared<SymbolTable>();
        previousScope_ = currentScope_;
        currentScope_  = name;
    }
    void leaveScope() {
        if (currentScope_->getParent()) {
            currentScope_ = currentScope_->getParent();
    void enter(const std::string & name) {
        if (scopes_.contains(name)) {
            previousScope_ = currentScope_;
            currentScope_  = name;
        } else {
            throw std::runtime_error("Scope does not exist: " + name);
        }
    }
    void define(const std::string& ns, const SymbolPtr& symbol) {
        currentScope_->define(ns, symbol);
    void enterPreviousScope() { currentScope_ = previousScope_; }
    [[nodiscard]] std::string currentScopeName() const { return currentScope_; }
    std::vector<std::string> getScopeNames() const {
        std::vector<std::string> result;
        result.reserve(scopes_.size());
        for (const auto & [scopeName, _] : scopes_) {
            result.push_back(scopeName);
        }
        return result;
    }
    SymbolPtr resolve(const std::string& ns, const std::string& name) const {
        return currentScope_->get(ns, name);
    // --- Symbol operations ---
    std::string add(const SymbolPtr & symbol) {
        const std::string ns = getNamespaceForSymbol(symbol);
        scopes_[currentScope_]->define(ns, symbol);
        return ns;
    }
    bool exists(const std::string& ns, const std::string& name) const {
        return currentScope_->exists(ns, name);
    std::vector<std::string> getNameSpaces(const std::string & scopeName) const {
        std::vector<std::string> result;
        auto                     it = scopes_.find(scopeName);
        if (it != scopes_.end()) {
            return it->second->listNSs();
        }
        return result;
    }
    std::vector<SymbolPtr> listNamespace(const std::string& ns) const {
        return currentScope_->listAll(ns);
    std::vector<SymbolPtr> getAll(const std::string & ns = "") const {
        std::vector<SymbolPtr> result;
        for (const auto & [_, table] : scopes_) {
            auto symbols = ns.empty() ? table->listAll() : table->listAll(ns);
            result.insert(result.end(), symbols.begin(), symbols.end());
        }
        return result;
    }
    std::shared_ptr<SymbolTable> getGlobalScope() const { return globalScope_; }
    std::shared_ptr<SymbolTable> getCurrentScope() const { return currentScope_; }
    bool exists(const std::string & name, std::string fullNamespace = "") const {
        if (fullNamespace.empty()) {
            fullNamespace = currentScope_;
        }
        for (const auto & [_, table] : scopes_) {
            if (table->exists(fullNamespace, name)) {
                return true;
            }
        }
        return false;
    }
    SymbolPtr get(const std::string & fullNamespace, const std::string & name) const {
        for (const auto & [_, table] : scopes_) {
            auto sym = table->get(fullNamespace, name);
            if (sym) {
                return sym;
            }
        }
        return nullptr;
    }
    static std::string dump() {
        std::string result = "";
        std::cout << "\n--- Defined Scopes ---" << '\n';
        for (const auto & scope_name : instance()->getScopeNames()) {
            result += scope_name + '\n';
            for (const auto & sname : instance()->getNameSpaces(scope_name)) {
                result += "\t -" + sname + '\n';
                for (const auto & symbol : instance()->getAll(sname)) {
                    result += symbol->dump() + '\n';
                }
            }
        }
        return result;
    }
  private:
    std::string getNamespaceForSymbol(const SymbolPtr & symbol) const {
        std::string base = symbol->context().empty() ? currentScope_ : symbol->context();
        switch (symbol->type()) {
            case Symbols::Kind::Variable:
                return base + ".variables";
            case Symbols::Kind::Function:
                return base + ".functions";
            case Symbols::Kind::Constant:
                return base + ".constants";
            default:
                return base + ".others";
        }
    }
};
} // namespace Symbols
src/Symbols/SymbolKind.hpp
@@ -3,6 +3,8 @@
#define SYMBOL_KIND_HPP
#include <cstdint>
#include <string>
#include <unordered_map>
namespace Symbols {
@@ -13,6 +15,19 @@
    // Later: Module, Class, etc..
};
static std::string kindToString(Symbols::Kind kind) {
    std::unordered_map<Symbols::Kind, std::string> KindToString = {
        { Symbols::Kind::Variable, "Variable" },
        { Symbols::Kind::Constant, "Constant" },
        { Symbols::Kind::Function, "Function" },
    };
    auto it = KindToString.find(kind);
    if (it != KindToString.end()) {
        return it->second;
    }
    return "Unknown kind: " + std::to_string(static_cast<int>(kind));
}
}; // namespace Symbols
#endif
src/Symbols/SymbolTable.hpp
@@ -1,4 +1,3 @@
// SymbolTable.hpp
#ifndef SYMBOL_TABLE_HPP
#define SYMBOL_TABLE_HPP
@@ -11,11 +10,8 @@
class SymbolTable {
    NamespaceMap                 symbols_;
    std::shared_ptr<SymbolTable> parent_ = nullptr;
  public:
    SymbolTable(std::shared_ptr<SymbolTable> parent = nullptr) : parent_(std::move(parent)) {}
    void define(const std::string & ns, const SymbolPtr & symbol) { symbols_[ns][symbol->name()] = symbol; }
    bool exists(const std::string & ns, const std::string & name) const { return get(ns, name) != nullptr; }
@@ -29,10 +25,6 @@
                return it->second;
            }
        }
        // Rekurzívan keresünk a szülő scope-ban
        if (parent_) {
            return parent_->get(ns, name);
        }
        return nullptr;
    }
@@ -43,12 +35,21 @@
        }
    }
    std::vector<SymbolPtr> listAll(const std::string & ns) const {
    std::vector<std::string> listNSs() {
        std::vector<std::string> result;
        for (const auto & [ns, _] : symbols_) {
            result.push_back(ns);
        }
        return result;
    }
    std::vector<SymbolPtr> listAll(const std::string & prefix = "") const {
        std::vector<SymbolPtr> result;
        auto                   it = symbols_.find(ns);
        if (it != symbols_.end()) {
            for (const auto & [_, sym] : it->second) {
        for (const auto & [ns, map] : symbols_) {
            if (prefix.empty() || ns.starts_with(prefix)) {
                for (const auto & [_, sym] : map) {
                result.push_back(sym);
                }
            }
        }
        return result;
@@ -57,8 +58,6 @@
    void clear(const std::string & ns) { symbols_.erase(ns); }
    void clearAll() { symbols_.clear(); }
    std::shared_ptr<SymbolTable> getParent() const { return parent_; }
};
}  // namespace Symbols
src/Symbols/Value.hpp
@@ -1,10 +1,13 @@
#ifndef SYMBOL_VALUE_HPP
#define SYMBOL_VALUE_HPP
#include <algorithm>
#include <iostream>
#include <stdexcept>
#include <string>
#include <variant>
#include "Symbols/VariableTypes.hpp"
namespace Symbols {
@@ -14,23 +17,32 @@
    Value() = default;
    Value(int v) : value_(v) {}
    Value(int v) : value_(v) { type_ = Symbols::Variables::Type::INTEGER; }
    Value(double v) : value_(v) {}
    Value(double v) : value_(v) { type_ = Symbols::Variables::Type::DOUBLE; }
    Value(float v) : value_(v) {}
    Value(float v) : value_(v) { type_ = Symbols::Variables::Type::FLOAT; }
    Value(const std::string & v) : value_(v) {}
    Value(const std::string & v) : value_(v) { type_ = Symbols::Variables::Type::STRING; }
    Value(const char * v) : value_(std::string(v)) {}
    Value(const char * v) : value_(std::string(v)) { type_ = Symbols::Variables::Type::STRING; }
    Value(bool v) : value_(v) {}
    Value(bool v) : value_(v) { type_ = Symbols::Variables::Type::BOOLEAN; }
    Value(const std::string & str, bool autoDetectType) { *this = fromString(str, autoDetectType); }
    const Variant & get() const { return value_; }
    Symbols::Variables::Type getType() const { return type_; }
    Variant & get() { return value_; }
    template <typename T> T get() const { return std::get<T>(value_); }
    static Symbols::Value makeNull() {
        auto v = Value("null");
        return v;
    }
    // operator+
    friend Value operator+(const Value & lhs, const Value & rhs) {
@@ -68,8 +80,87 @@
    static std::string to_string(const Value & val) { return to_string(val.value_); }
    static Value fromString(const std::string & str, bool autoDetectType) {
        if (!autoDetectType) {
            return fromStringToString(str);
        }
        std::string trimmed = str;
        trimmed.erase(0, trimmed.find_first_not_of(" \t\n\r"));
        trimmed.erase(trimmed.find_last_not_of(" \t\n\r") + 1);
        // Check bool
        std::string lower = trimmed;
        std::transform(lower.begin(), lower.end(), lower.begin(), ::tolower);
        if (lower == "true" || lower == "false" || lower == "1" || lower == "0") {
            try {
                return fromStringToBool(trimmed);
            } catch (...) {
            }
        }
        // Check int
        try {
            size_t idx;
            int    i = std::stoi(trimmed, &idx);
            if (idx == trimmed.size()) {
                return fromStringToInt(trimmed);
            }
        } catch (...) {
        }
        // Check double
        try {
            size_t idx;
            double d = std::stod(trimmed, &idx);
            if (idx == trimmed.size()) {
                return fromStringToDouble(trimmed);
            }
        } catch (...) {
        }
        // Fallback
        return fromStringToString(str);
    }
    static Value fromString(const std::string & str, Symbols::Variables::Type type) {
        switch (type) {
            case Symbols::Variables::Type::INTEGER:
                return fromStringToInt(str);
            case Symbols::Variables::Type::DOUBLE:
                return fromStringToDouble(str);
            case Symbols::Variables::Type::FLOAT:
                return fromStringToFloat(str);
            case Symbols::Variables::Type::BOOLEAN:
                return fromStringToBool(str);
            case Symbols::Variables::Type::STRING:
            default:
                return fromStringToString(str);
        }
    }
  private:
    Variant value_;
    Symbols::Variables::Type type_;
    static Value fromStringToInt(const std::string & str) { return Value(std::stoi(str)); }
    static Value fromStringToDouble(const std::string & str) { return Value(std::stod(str)); }
    static Value fromStringToFloat(const std::string & str) { return Value(std::stof(str)); }
    static Value fromStringToBool(const std::string & str) {
        std::string s = str;
        std::transform(s.begin(), s.end(), s.begin(), ::tolower);
        if (s == "true" || s == "1") {
            return Value(true);
        }
        if (s == "false" || s == "0") {
            return Value(false);
        }
        throw std::invalid_argument("Invalid bool string: " + str);
    }
    static Value fromStringToString(const std::string & str) { return Value(str); }
};
}  // namespace Symbols
src/VoidScript.hpp
@@ -5,74 +5,68 @@
#include <fstream>
#include <string>
#include "Interpreter/Interpreter.hpp"
#include "Lexer/Lexer.hpp"
#include "Parser/Parser.hpp"
class VoidScript {
  private:
    std::string                     file;
    std::vector<std::string>        files;
    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) {
    static std::string readFile(const std::string & 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);
            return "";
        }
        file_content = std::string((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
        std::string content = std::string((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
        input.close();
        return content;
    }
  public:
    VoidScript(const std::string & file) :
        lexer(std::make_shared<Lexer::Lexer>()),
        parser(std::make_shared<Parser::Parser>()) {
        this->files.emplace(this->files.begin(), file);
        lexer->setKeyWords(Parser::Parser::keywords);
    }
    int run() {
        try {
            this->lexer->addNamespaceInput(this->file, this->file_content);
            const auto tokens = this->lexer->tokenizeNamespace(this->file);
            while (!files.empty()) {
                std::string file = files.back();
                const std::string file_content = readFile(file);
                files.pop_back();
            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::string _default_namespace_ = file;
                std::replace(_default_namespace_.begin(), _default_namespace_.end(), '.', '_');
            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';
                }
            }
                Symbols::SymbolContainer::instance()->create(_default_namespace_);
            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';
                }
            }
                const std::string ns = Symbols::SymbolContainer::instance()->currentScopeName();
                this->lexer->addNamespaceInput(ns, file_content);
                const auto tokens = this->lexer->tokenizeNamespace(ns);
                std::cout << Operations::Container::dump() << "\n";
                parser->parseScript(tokens, file_content, file);
                Interpreter::Interpreter interpreter;
                interpreter.run();
                std::cout << Symbols::SymbolContainer::dump() << "\n";
            }  // while (!files.empty())
            return 0;
        } catch (const Parser::SyntaxError & e) {