A simple scripting language in C++
Ferenc Szontágh
2025-04-14 d7cd4947b37a168034e9fca2501d98553fdcc137
refactor phase1
6 files modified
1 files renamed
11 files added
2 files deleted
1093 ■■■■ changed files
CMakeLists.txt 5 ●●●●● patch | view | raw | blame | history
include/BaseFunction.hpp 1 ●●●● patch | view | raw | blame | history
include/BaseSymbol.hpp 51 ●●●●● patch | view | raw | blame | history
include/ConstantSymbol.hpp 28 ●●●●● patch | view | raw | blame | history
include/FunctionSymbol.hpp 31 ●●●●● patch | view | raw | blame | history
include/SymbolContainer.hpp 51 ●●●●● patch | view | raw | blame | history
include/SymbolFactory.hpp 58 ●●●●● patch | view | raw | blame | history
include/SymbolKind.hpp 18 ●●●●● patch | view | raw | blame | history
include/SymbolTable.hpp 67 ●●●●● patch | view | raw | blame | history
include/SymbolTypes.hpp 22 ●●●●● patch | view | raw | blame | history
include/VariableSymbol.hpp 20 ●●●●● patch | view | raw | blame | history
include/VariableTypes.hpp 46 ●●●●● patch | view | raw | blame | history
src/Lexer.cpp 90 ●●●● patch | view | raw | blame | history
src/Lexer.hpp 8 ●●●●● patch | view | raw | blame | history
src/ScriptInterpreter.cpp 100 ●●●●● patch | view | raw | blame | history
src/ScriptInterpreterHelpers.hpp 11 ●●●● patch | view | raw | blame | history
src/StringHelpers.hpp 20 ●●●●● patch | view | raw | blame | history
src/Token.hpp 190 ●●●● patch | view | raw | blame | history
src/Value.hpp 212 ●●●●● patch | view | raw | blame | history
src/VariableTypes.hpp 64 ●●●●● patch | view | raw | blame | history
CMakeLists.txt
@@ -97,8 +97,6 @@
        set(ARCHITECTURE "amd64")
    elseif(CMAKE_SIZEOF_VOID_P EQUAL 4)
        set(ARCHITECTURE "i386")
    else()
        message(STATUS "Célarchitektúra: Nem meghatározható (CMAKE_SIZEOF_VOID_P: ${CMAKE_SIZEOF_VOID_P})")
    endif()
endif()
@@ -115,7 +113,8 @@
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 src)
include_directories(${CMAKE_BINARY_DIR}/include ${CMAKE_SOURCE_DIR}/src ${CMAKE_SOURCE_DIR}/include)
# LIBRARY TARGET
add_library(voidscript
include/BaseFunction.hpp
File was renamed from src/BaseFunction.hpp
@@ -2,7 +2,6 @@
#define SCRIPT_FUNCTION_HPP
#include <functional>
#include <stdexcept>
#include <unordered_map>
#include <utility>
#include <vector>
include/BaseSymbol.hpp
New file
@@ -0,0 +1,51 @@
// BaseSymbol.hpp
#ifndef BASE_SYMBOL_HPP
#define BASE_SYMBOL_HPP
#include <string>
#include <variant>
#include "SymbolKind.hpp"
namespace Symbols {
using Value = std::variant<int, double, std::string, bool>;
class Symbol {
  protected:
    std::string   name_;
    Value         value_;
    std::string   context_;
    Symbols::Kind kind_;
  public:
    Symbol(const std::string & name, const Value & value, const std::string & context, Symbols::Kind type) :
        name_(name),
        value_(value),
        context_(context),
        kind_(type) {}
    virtual ~Symbol() = default;
    // Polimorf azonosító
    virtual Symbols::Kind kind() const = 0;
    // Getterek
    const std::string & name() const { return name_; }
    const std::string & context() const { return context_; }
    Symbols::Kind type() const { return kind_; }
    // Virtuális getter/setter a value-hoz
    virtual const Value & getValue() const { return value_; }
    virtual void setValue(const Value & value) { value_ = value; }
    // Templated getter
    template <typename T> T getAs() const { return std::get<T>(value_); }
};
}  // namespace Symbols
#endif
include/ConstantSymbol.hpp
New file
@@ -0,0 +1,28 @@
// ConstantSymbol.hpp
#ifndef CONSTANT_SYMBOL_HPP
#define CONSTANT_SYMBOL_HPP
#include <stdexcept>
#include "BaseSymbol.hpp"
#include "VariableTypes.hpp"
namespace Symbols {
class ConstantSymbol : public Symbol {
  protected:
    Symbols::Variables::Type _vartype;
  public:
    ConstantSymbol(const std::string & name, const Symbols::Value & value, const std::string & context) :
        Symbol(name, value, context, Symbols::Kind::Constant) {}
    Symbols::Kind kind() const override { return Symbols::Kind::Constant; }
    void setValue(const Symbols::Value & /*value*/) override {
        throw std::logic_error("Cannot modify a constant symbol");
    }
};
}  // namespace Symbols
#endif
include/FunctionSymbol.hpp
New file
@@ -0,0 +1,31 @@
// FunctionSymbol.hpp
#ifndef FUNCTION_SYMBOL_HPP
#define FUNCTION_SYMBOL_HPP
#include <vector>
#include "BaseSymbol.hpp"
namespace Symbols {
using ValueContainer = std::vector<Symbols::Value>;
class FunctionSymbol : public Symbol {
    std::vector<Symbols::Value> parameters_;
    Symbols::Value              returnType_;
    std::string                 plainBody_;
  public:
    FunctionSymbol(const std::string & name, const std::string & context, const ValueContainer & parameters,
                   const std::string & plainbody = "") :
        Symbol(name, {}, context, Symbols::Kind::Function),
        parameters_(parameters),
        plainBody_(plainbody) {}
    Symbols::Kind kind() const override { return Symbols::Kind::Function; }
    const ValueContainer & parameters() const { return parameters_; }
};
}  // namespace Symbols
#endif
include/SymbolContainer.hpp
New file
@@ -0,0 +1,51 @@
// SymbolContainer.hpp
#ifndef SYMBOL_CONTAINER_HPP
#define SYMBOL_CONTAINER_HPP
#include "SymbolTable.hpp"
namespace Symbols {
class SymbolContainer {
    std::shared_ptr<SymbolTable> globalScope_;
    std::shared_ptr<SymbolTable> currentScope_;
public:
    SymbolContainer() {
        globalScope_ = std::make_shared<SymbolTable>();
        currentScope_ = globalScope_;
    }
    void enterScope() {
        currentScope_ = std::make_shared<SymbolTable>(currentScope_);
    }
    void leaveScope() {
        if (currentScope_->getParent()) {
            currentScope_ = currentScope_->getParent();
        }
    }
    void define(const std::string& ns, const SymbolPtr& symbol) {
        currentScope_->define(ns, symbol);
    }
    SymbolPtr resolve(const std::string& ns, const std::string& name) const {
        return currentScope_->get(ns, name);
    }
    bool exists(const std::string& ns, const std::string& name) const {
        return currentScope_->exists(ns, name);
    }
    std::vector<SymbolPtr> listNamespace(const std::string& ns) const {
        return currentScope_->listAll(ns);
    }
    std::shared_ptr<SymbolTable> getGlobalScope() const { return globalScope_; }
    std::shared_ptr<SymbolTable> getCurrentScope() const { return currentScope_; }
};
} // namespace Symbols
#endif
include/SymbolFactory.hpp
New file
@@ -0,0 +1,58 @@
// SymbolFactory.hpp
#ifndef SYMBOL_FACTORY_HPP
#define SYMBOL_FACTORY_HPP
#include <memory>
#include <string>
#include "ConstantSymbol.hpp"
#include "FunctionSymbol.hpp"
#include "VariableSymbol.hpp"
namespace Symbols {
class SymbolFactory {
  public:
    static std::shared_ptr<Symbol> createVariable(const std::string & name, const Symbols::Value & value,
                                                  const std::string & context, Variables::Type type) {
        return std::make_shared<VariableSymbol>(name, value, context, type);
    }
    static std::shared_ptr<Symbol> createConstant(const std::string & name, const Symbols::Value & value,
                                                  const std::string & context) {
        return std::make_shared<ConstantSymbol>(name, value, context);
    }
    static std::shared_ptr<Symbol> createFunction(const std::string & name, const std::string & context,
                                                  const Symbols::ValueContainer & 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) {
        return std::make_shared<FunctionSymbol>(name, context, parameters, plainBody);
    }
    // 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);
    }
    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);
    }
    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);
    }
    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);
    }
};
}  // namespace Symbols
#endif
include/SymbolKind.hpp
New file
@@ -0,0 +1,18 @@
// SymbolKind.hpp
#ifndef SYMBOL_KIND_HPP
#define SYMBOL_KIND_HPP
#include <cstdint>
namespace Symbols {
enum class Kind : std::uint8_t {
    Variable,
    Constant,
    Function
    // Later: Module, Class, etc..
};
}; // namespace Symbols
#endif
include/SymbolTable.hpp
New file
@@ -0,0 +1,67 @@
// SymbolTable.hpp
#ifndef SYMBOL_TABLE_HPP
#define SYMBOL_TABLE_HPP
#include <memory>
#include <optional>
#include <vector>
#include "SymbolTypes.hpp"
namespace Symbols {
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; }
    SymbolPtr get(const std::string & ns, const std::string & name) const {
        auto itNs = symbols_.find(ns);
        if (itNs != symbols_.end()) {
            const auto & map = itNs->second;
            auto         it  = map.find(name);
            if (it != map.end()) {
                return it->second;
            }
        }
        // Rekurzívan keresünk a szülő scope-ban
        if (parent_) {
            return parent_->get(ns, name);
        }
        return nullptr;
    }
    void remove(const std::string & ns, const std::string & name) {
        auto itNs = symbols_.find(ns);
        if (itNs != symbols_.end()) {
            itNs->second.erase(name);
        }
    }
    std::vector<SymbolPtr> listAll(const std::string & ns) const {
        std::vector<SymbolPtr> result;
        auto                   it = symbols_.find(ns);
        if (it != symbols_.end()) {
            for (const auto & [_, sym] : it->second) {
                result.push_back(sym);
            }
        }
        return result;
    }
    void clear(const std::string & ns) { symbols_.erase(ns); }
    void clearAll() { symbols_.clear(); }
    std::shared_ptr<SymbolTable> getParent() const { return parent_; }
};
}  // namespace Symbols
#endif
include/SymbolTypes.hpp
New file
@@ -0,0 +1,22 @@
// SymbolTypes.hpp
#ifndef SYMBOL_TYPES_HPP
#define SYMBOL_TYPES_HPP
#include <memory>
#include <string>
#include <unordered_map>
#include "BaseSymbol.hpp"
namespace Symbols {
using SymbolPtr = std::shared_ptr<Symbol>;
// Namespace -> név -> szimbólum
using SymbolMap    = std::unordered_map<std::string, SymbolPtr>;
using NamespaceMap = std::unordered_map<std::string, SymbolMap>;
}  // namespace Symbols
#endif
include/VariableSymbol.hpp
New file
@@ -0,0 +1,20 @@
// VariableSymbol.hpp
#ifndef VARIABLE_SYMBOL_HPP
#define VARIABLE_SYMBOL_HPP
#include "BaseSymbol.hpp"
namespace Symbols {
class VariableSymbol : public Symbol {
  public:
    VariableSymbol(const std::string & name, const Symbols::Value & value, const std::string & context,
                   Symbols::Kind type) :
        Symbol(name, value, context, type) {}
    Symbols::Kind kind() const override { return Symbols::Kind::Variable; }
};
}  // namespace Symbols
#endif
include/VariableTypes.hpp
New file
@@ -0,0 +1,46 @@
#ifndef VARIABLE_TYPES_HPP
#define VARIABLE_TYPES_HPP
#include <cstdint>
#include <string>
#include <unordered_map>
namespace Symbols::Variables {
enum class Type : std::uint8_t { VT_INT, VT_DOUBLE, VT_STRING, VT_BOOLEAN, VT_NULL, VT_UNDEFINED };
const std::unordered_map<std::string, Type> StringToTypeMap = {
    { "int",       Type::VT_INT       },
    { "double",    Type::VT_DOUBLE    },
    { "string",    Type::VT_STRING    },
    { "bool",      Type::VT_BOOLEAN   },
    { "boolean",   Type::VT_BOOLEAN   },
    { "null",      Type::VT_NULL      },
    { "undefined", Type::VT_UNDEFINED },
};
const std::unordered_map<Type, std::string> StypeToStringMap = {
    { Type::VT_INT,       "int"        },
    { Type::VT_DOUBLE,    "double"     },
    { Type::VT_STRING,    "string"     },
    { Type::VT_BOOLEAN,   "bool"       },
    { Type::VT_NULL,      "null"       },
    { Type::VT_UNDEFINED, "undeffined" },
};
inline static std::string TypeToString(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::VT_NULL;
};
};  // namespace Symbols
#endif  // VARIABLE_TYPES_HPP
src/Lexer.cpp
@@ -40,14 +40,14 @@
    return pos >= src.size();
}
Token Lexer::createToken(TokenType type, const std::string & lexeme) const {
Tokens::Token Lexer::createToken(Tokens::Type type, const std::string & lexeme) const {
    size_t startChar = charNumber - lexeme.length();
    return {
        type, lexeme, filename, lineNumber, colNumber - lexeme.length(), { startChar, charNumber }
    };
}
Token Lexer::createSingleCharToken(TokenType type, const std::string & lexeme) {
Tokens::Token Lexer::createSingleCharToken(Tokens::Type type, const std::string & lexeme) {
    size_t startCol  = colNumber;
    size_t startChar = charNumber;
    advance();
@@ -56,14 +56,14 @@
    };
}
Token Lexer::createUnknownToken(const std::string & lexeme) const {
Tokens::Token Lexer::createUnknownToken(const std::string & lexeme) const {
    size_t startChar = charNumber - lexeme.length();
    return {
        TokenType::Unknown, lexeme, filename, lineNumber, colNumber - lexeme.length(), { startChar, charNumber }
        Tokens::Type::Unknown, lexeme, filename, lineNumber, colNumber - lexeme.length(), { startChar, charNumber }
    };
}
Token Lexer::stringToken() {
Tokens::Token Lexer::stringToken() {
    std::string result;
    size_t      startChar = charNumber;
    size_t      startCol  = colNumber;
@@ -73,19 +73,19 @@
    }
    if (isAtEnd() || peek() != '"') {
        return {
            TokenType::Unknown, "Unterminated string", filename, lineNumber, startCol, { startChar, pos }
            Tokens::Type::Unknown, "Unterminated string", filename, lineNumber, startCol, { startChar, pos }
        };
    }
    advance();  // Skip closing quote
    return {
        TokenType::StringLiteral, result, filename, lineNumber, startCol, { startChar, pos }
        Tokens::Type::StringLiteral, result, filename, lineNumber, startCol, { startChar, pos }
    };
}
Token Lexer::numberToken() {
Tokens::Token Lexer::numberToken() {
    std::string result;
    std::string found;
    TokenType   type             = TokenType::Unknown;
    Tokens::Type type             = Tokens::Type::Unknown;
    bool        decimalPointSeen = false;
    size_t      startChar        = charNumber;
    size_t      startCol         = colNumber;
@@ -94,7 +94,7 @@
        if (peek() == '.') {
            if (decimalPointSeen) {
                return {
                    TokenType::Unknown, "Invalid number format", filename, lineNumber, startCol, { startChar, pos }
                    Tokens::Type::Unknown, "Invalid number format", filename, lineNumber, startCol, { startChar, pos }
                };
            }
            decimalPointSeen = true;
@@ -106,25 +106,25 @@
        if (found.find('.') == std::string::npos) {
            if (is_number<int>(found)) {
                result = found;
                type   = TokenType::IntLiteral;
                type   = Tokens::Type::IntLiteral;
            } else {
                return {
                    TokenType::Unknown, "Invalid integer", filename, lineNumber, startCol, { startChar, pos }
                    Tokens::Type::Unknown, "Invalid integer", filename, lineNumber, startCol, { startChar, pos }
                };
            }
        } else {
            if (is_number<double>(found)) {
                result = found;
                type   = TokenType::DoubleLiteral;
                type   = Tokens::Type::DoubleLiteral;
            } else {
                return {
                    TokenType::Unknown, "Invalid double", filename, lineNumber, startCol, { startChar, pos }
                    Tokens::Type::Unknown, "Invalid double", filename, lineNumber, startCol, { startChar, pos }
                };
            }
        }
    } else {
        return {
            TokenType::Unknown, "Expected number", filename, lineNumber, startCol, { startChar, pos }
            Tokens::Type::Unknown, "Expected number", filename, lineNumber, startCol, { startChar, pos }
        };
    }
@@ -133,7 +133,7 @@
    };
}
Token Lexer::identifierToken() {
Tokens::Token Lexer::identifierToken() {
    std::string result;
    size_t      startChar = charNumber;
    size_t      startCol  = colNumber;
@@ -141,11 +141,11 @@
        result += advance();
    }
    return {
        TokenType::Identifier, result, filename, lineNumber, startCol, { startChar, pos }
        Tokens::Type::Identifier, result, filename, lineNumber, startCol, { startChar, pos }
    };
}
Token Lexer::variableToken() {
Tokens::Token Lexer::variableToken() {
    size_t startChar = charNumber;
    size_t startCol  = colNumber;
    advance();  // Skip $
@@ -156,15 +156,15 @@
            varName += advance();
        }
        return {
            TokenType::Variable, varName, filename, lineNumber, startCol, { startChar, pos }
            Tokens::Type::Variable, varName, filename, lineNumber, startCol, { startChar, pos }
        };
    }
    return {
        TokenType::Unknown, "$ followed by invalid character", filename, lineNumber, startCol, { startChar, pos }
        Tokens::Type::Unknown, "$ followed by invalid character", filename, lineNumber, startCol, { startChar, pos }
    };
}
Token Lexer::commentToken() {
Tokens::Token Lexer::commentToken() {
    size_t startChar = charNumber;
    size_t startCol  = colNumber;
    advance();  // Skip #
@@ -173,11 +173,11 @@
        commentText += advance();
    }
    return {
        TokenType::Comment, commentText, filename, lineNumber, startCol, { startChar, pos }
        Tokens::Type::Comment, commentText, filename, lineNumber, startCol, { startChar, pos }
    };
}
Token Lexer::keywordOrIdentifierToken() {
Tokens::Token Lexer::keywordOrIdentifierToken() {
    std::string lexeme;
    while (isalpha(peek())) {
        lexeme += advance();
@@ -187,14 +187,14 @@
    }
    if (lexeme == IDENTIFIER_RETURN) {
        return createToken(TokenType::Return, lexeme);
        return createToken(Tokens::Type::Return, lexeme);
    }
    if (lexeme == IDENTIFIER_IF) {
        return createToken(TokenType::ParserIfStatement, lexeme);
        return createToken(Tokens::Type::ParserIfStatement, lexeme);
    }
    if (peek() == '(') {  // Function call
        return createToken(TokenType::FunctionCall, lexeme);
        return createToken(Tokens::Type::FunctionCall, lexeme);
    }
    auto it = Variables::StringToTypeMap.find(lexeme);
@@ -207,12 +207,12 @@
        if (peek() == IDENTIFIER_VARIABLE) {
            return this->variableDeclarationToken(type);
        }
        return createToken(TokenType::Identifier, lexeme);
        return createToken(Tokens::Type::Identifier, lexeme);
    }
    return createToken(TokenType::Identifier, lexeme);
    return createToken(Tokens::Type::Identifier, lexeme);
}
Token Lexer::functionDeclarationToken() {
Tokens::Token Lexer::functionDeclarationToken() {
    advance();  // Skip function
    std::string functionName;
    if (isalpha(peek()) || peek() == '_') {
@@ -220,12 +220,12 @@
        while (isalnum(peek()) || peek() == '_') {
            functionName += advance();
        }
        return createToken(TokenType::FunctionDeclaration, functionName);
        return createToken(Tokens::Type::FunctionDeclaration, functionName);
    }
    return createUnknownToken("function followed by invalid character");
}
Token Lexer::variableDeclarationToken(Variables::Type type) {
Tokens::Token Lexer::variableDeclarationToken(Variables::Type type) {
    advance();  // Skip $
    std::string varName;
    if (isalpha(peek()) || peek() == '_') {
@@ -269,8 +269,8 @@
    }
}
std::vector<Token> Lexer::tokenize() {
    std::vector<Token> tokens;
std::vector<Tokens::Token> Lexer::tokenize() {
    std::vector<Tokens::Token> tokens;
    tokens.reserve(src.size() / 4);
    while (pos < src.size()) {
@@ -281,7 +281,7 @@
            continue;
        }
        if (c == '\n') {
            tokens.push_back(createSingleCharToken(TokenType::EndOfLine, "\n"));
            tokens.push_back(createSingleCharToken(Tokens::Type::EndOfLine, "\n"));
            continue;
        }
        if (c == IDENTIFIER_COMMENT) {
@@ -291,12 +291,12 @@
        }
        if (matchSequence(PARSER_OPEN_TAG)) {
            matchAndConsume(PARSER_OPEN_TAG);
            tokens.push_back(createToken(TokenType::ParserOpenTag, PARSER_OPEN_TAG));
            tokens.push_back(createToken(Tokens::Type::ParserOpenTag, PARSER_OPEN_TAG));
            continue;
        }
        if (matchSequence(PARSER_CLOSE_TAG)) {
            matchAndConsume(PARSER_CLOSE_TAG);
            tokens.push_back(createToken(TokenType::ParserCloseTag, PARSER_CLOSE_TAG));
            tokens.push_back(createToken(Tokens::Type::ParserCloseTag, PARSER_CLOSE_TAG));
            continue;
        }
        switch (c) {
@@ -315,28 +315,28 @@
                tokens.push_back(stringToken());
                break;
            case '(':
                tokens.push_back(createSingleCharToken(TokenType::LeftParenthesis, "("));
                tokens.push_back(createSingleCharToken(Tokens::Type::LeftParenthesis, "("));
                break;
            case ')':
                tokens.push_back(createSingleCharToken(TokenType::RightParenthesis, ")"));
                tokens.push_back(createSingleCharToken(Tokens::Type::RightParenthesis, ")"));
                break;
            case ',':
                tokens.push_back(createSingleCharToken(TokenType::Comma, ","));
                tokens.push_back(createSingleCharToken(Tokens::Type::Comma, ","));
                break;
            case ';':
                tokens.push_back(createSingleCharToken(TokenType::Semicolon, ";"));
                tokens.push_back(createSingleCharToken(Tokens::Type::Semicolon, ";"));
                break;
            case '=':
                tokens.push_back(createSingleCharToken(TokenType::Equals, "="));
                tokens.push_back(createSingleCharToken(Tokens::Type::Equals, "="));
                break;
            case '+':
                tokens.push_back(createSingleCharToken(TokenType::Plus, "+"));
                tokens.push_back(createSingleCharToken(Tokens::Type::Plus, "+"));
                break;
            case '{':
                tokens.push_back(createSingleCharToken(TokenType::LeftCurlyBracket, "{"));
                tokens.push_back(createSingleCharToken(Tokens::Type::LeftCurlyBracket, "{"));
                break;
            case '}':
                tokens.push_back(createSingleCharToken(TokenType::RightCurlyBracket, "}"));
                tokens.push_back(createSingleCharToken(Tokens::Type::RightCurlyBracket, "}"));
                break;
            default:
                tokens.push_back(createUnknownToken(std::string(1, c)));
@@ -346,7 +346,7 @@
    }
    tokens.push_back({
        TokenType::EndOfFile, "", filename, lineNumber, colNumber, { charNumber, charNumber }
        Tokens::Type::EndOfFile, "", filename, lineNumber, colNumber, { charNumber, charNumber }
    });
    return tokens;
}
src/Lexer.hpp
@@ -6,6 +6,7 @@
#include <sstream>
#include <vector>
#include "StringHelpers.hpp"
#include "Token.hpp"
#include "VariableTypes.hpp"
@@ -66,11 +67,8 @@
        std::string srcSubstr = src.substr(pos, sequence.length());
        std::string seqLower  = sequence;
        std::transform(srcSubstr.begin(), srcSubstr.end(), srcSubstr.begin(),
                       [](unsigned char c) { return std::tolower(c); });
        std::transform(seqLower.begin(), seqLower.end(), seqLower.begin(),
                       [](unsigned char c) { return std::tolower(c); });
        StringHelpers::strtolower(srcSubstr);
        StringHelpers::strtolower(seqLower);
        return srcSubstr == seqLower;
    }
src/ScriptInterpreter.cpp
@@ -14,10 +14,11 @@
}
Value ScriptInterpreter::evaluateExpression(const Token & token) const {
    if (token.type == TokenType::StringLiteral) {
    switch (token.type) {
        case TokenType::StringLiteral:
        return Value::fromString(token);
    }
    if (token.type == TokenType::IntLiteral) {
        case TokenType::IntLiteral:
        try {
            return Value::fromInt(token);
        } catch (const std::invalid_argument & e) {
@@ -25,8 +26,8 @@
        } catch (const std::out_of_range & e) {
            throw std::runtime_error("Integer literal out of range: " + token.lexeme);
        }
    }
    if (token.type == TokenType::DoubleLiteral) {
        case TokenType::DoubleLiteral:
        try {
            return Value::fromDouble(token);
        } catch (const std::invalid_argument & e) {
@@ -34,17 +35,19 @@
        } catch (const std::out_of_range & e) {
            throw std::runtime_error("Double literal out of range: " + token.lexeme);
        }
        case TokenType::BooleanLiteral:
        case TokenType::Identifier:
            {
                return Value::fromBoolean(token);
    }
    if (token.type == TokenType::BooleanLiteral || token.type == TokenType::Identifier) {
        std::string lowered = token.lexeme;
        std::transform(lowered.begin(), lowered.end(), lowered.begin(),
                       [](unsigned char c) { return std::tolower(c); });
        return Value::fromBoolean(token, (lowered == "true" ? true : false));
    }
    if (token.type == TokenType::Variable) {
        case TokenType::Variable:
        return this->getVariable(token, this->contextPrefix, __FILE__, __LINE__);
    }
        default:
    THROW_UNEXPECTED_TOKEN_ERROR(token, "string, integer, double, or variable");
    }
    return Value();
}
@@ -81,62 +84,39 @@
    return args;
}
void ScriptInterpreter::handleBooleanDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
    const auto & varToken = tokens[i];
    i++;      // Skip variable name
    if (i < tokens.size() && tokens[i].type == TokenType::Equals) {
        i++;  // Skip '='
        if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
            auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
            if (variable.type != Variables::Type::VT_BOOLEAN) {
                THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme,
                                                    Variables::TypeToString(Variables::Type::VT_BOOLEAN),
                                                    tokens[i].lexeme, variable.TypeToString(), tokens[i]);
void ScriptInterpreter::handleBooleanDeclaration(const std::vector<Token> & tokens, size_t & i) {
    if (tokens.size() <= i || tokens[i].type != TokenType::Identifier) {
        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "variable name");
            }
            this->setVariable(varToken.lexeme, variable, this->contextPrefix, true);
            i++;  // Skip variable name
            EXPECT_SEMICOLON(tokens, i, "after bool variable declaration");
        } else if (i < tokens.size() &&
                   (tokens[i].type == TokenType::Identifier || tokens[i].type == TokenType::StringLiteral)) {
            std::string lowered = tokens[i].lexeme;
            std::transform(lowered.begin(), lowered.end(), lowered.begin(),
                           [](unsigned char c) { return std::tolower(c); });
    const Token & varToken = tokens[i++];
    if (i >= tokens.size()) {
        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i + 1], "semicolon or assignment after variable name");
    }
            if (lowered == "true") {
                auto value = Value::fromBoolean(tokens[i], true);
                this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
            } else if (lowered == "false") {
                auto value = Value::fromBoolean(tokens[i], false);
                this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
            } else {
                THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "true or false after '='");
    if (tokens[i].type == TokenType::Semicolon) {
        auto val = Value::fromBoolean(tokens[i]);
        this->setVariable(varToken.lexeme, val, this->contextPrefix, true);
        i++;
    } else if (tokens[i].type == TokenType::Equal) {
        i++;
        if (i >= tokens.size()) {
            //throw InterpreterException("Expected value after assignment.", tokens[i - 1]);
            THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "value after assignment");
            }
            i++;  // Skip boolean literal
            EXPECT_SEMICOLON(tokens, i, "after bool declaration");
        } else if (i < tokens.size() && tokens[i].type == TokenType::IntLiteral) {
            const auto test = std::stoi(tokens[i].lexeme);
            if (test == 0) {
                auto value = Value::fromBoolean(tokens[i], false);
                this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
            } else if (test > 0) {
                auto value = Value::fromBoolean(tokens[i], false);
                this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
            } else {
                THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "bool literal after '='");
        auto value = evaluateExpression(tokens[i]);
        if (value.type != Variables::Type::VT_BOOLEAN) {
            THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme, Variables::TypeToString(Variables::Type::VT_BOOLEAN),
                                                tokens[i].lexeme, value.TypeToString(), tokens[i]);
            }
        this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
            i++;
            EXPECT_SEMICOLON(tokens, i, "after bool declaration");
        } else {
            THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "bool literal after '='");
        EXPECT_SEMICOLON(tokens, i, "after bool declaration");
        }
    } else {
        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after bool declaration");
    }
};
void ScriptInterpreter::handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
    const auto & varToken = tokens[i];
@@ -242,7 +222,7 @@
    size_t end;
    ScriptInterpreterHelpers::getFunctionBody(tokens, i, start, end);
    const std::string function_body = ScriptInterpreterHelpers::extractSubstring(this->source, start, end);
    const std::string function_body = StringHelpers::extractSubstring(this->source, start, end);
    if (function_body.empty()) {
        std::cout << this->source << '\n';
        THROW_FUNCTION_BODY_EMPTY(funcToken.lexeme, tokens[i - 1]);
src/ScriptInterpreterHelpers.hpp
@@ -1,8 +1,7 @@
#ifndef SCRIPTINTERPRETERHELPERS_HPP
#define SCRIPTINTERPRETERHELPERS_HPP
#include <iostream>
#include <ostream>
#include <algorithm>
#include <vector>
#include "ScriptExceptionMacros.h"
@@ -14,12 +13,7 @@
namespace ScriptInterpreterHelpers {
static std::string extractSubstring(const std::string & str, const size_t & start, const size_t & end) {
    if (start >= 0 && start < str.length() && end >= start && end < str.length()) {
        return str.substr(start, end - start + 1);
    }
    return "";
}
static void expectSemicolon(const std::vector<Token> & tokens, std::size_t & i, const std::string & message,
                            const std::string & file = __FILE__, int line = __LINE__) {
@@ -98,6 +92,7 @@
    }
};
};  // namespace ScriptInterpreterHelpers
#endif  // SCRIPTINTERPRETERHELPERS_HPP
src/StringHelpers.hpp
New file
@@ -0,0 +1,20 @@
#ifndef STRINGHELPERS_HPP
#define STRINGHELPERS_HPP
#include <algorithm>
#include <string>
namespace StringHelpers {
static std::string extractSubstring(const std::string & str, const size_t & start, const size_t & end) {
    if (start >= 0 && start < str.length() && end >= start && end < str.length()) {
        return str.substr(start, end - start + 1);
    }
    return "";
}
static void strtolower(std::string & data) {
    std::transform(data.begin(), data.end(), data.begin(), [](unsigned char c) { return std::tolower(c); });
}
};  // namespace StringHelpers
#endif  // STRINGHELPERS_HPP
src/Token.hpp
@@ -1,13 +1,11 @@
#ifndef TOKEN_HPP
#define TOKEN_HPP
#include <cstdint>
#include <iostream>
#include <string>
#include <unordered_map>
#include "VariableTypes.hpp"
enum class TokenType : std::uint8_t {
namespace Tokens {
enum class Type : std::uint8_t {
    ParserOpenTag,
    ParserCloseTag,
    ParserIfStatement,  // if
@@ -55,135 +53,52 @@
    Unknown               // Unknown
};
const static std::unordered_map<TokenType, std::string> tokenTypeNames = {
    { TokenType::ParserOpenTag,       "ParserOpenTag"       },
    { TokenType::ParserCloseTag,      "ParserCloseTag"      },
    { TokenType::ParserIfStatement,   "ParserIfStatement"   },
    { TokenType::FileClose,           "FileClose"           },
    { TokenType::Identifier,          "Identifier"          },
    { TokenType::StringLiteral,       "StringLiteral"       },
    { TokenType::IntLiteral,          "IntLiteral"          },
    { TokenType::DoubleLiteral,       "DoubleLiteral"       },
    { TokenType::BooleanLiteral,      "BooleanLiteral"      },
    { TokenType::LeftParenthesis,     "LeftParenthesis"     },
    { TokenType::RightParenthesis,    "RightParenthesis"    },
    { TokenType::Comma,               "Comma"               },
    { TokenType::Semicolon,           "Semicolon"           },
    { TokenType::Variable,            "Variable"            },
    { TokenType::VariableSign,        "VariableSign"        },
    { TokenType::StringDeclaration,   "StringDeclaration"   },
    { TokenType::IntDeclaration,      "IntDeclaration"      },
    { TokenType::DoubleDeclaration,   "DoubleDeclaration"   },
    { TokenType::BooleanDeclaration,  "BooleanDeclaration"  },
    { TokenType::FunctionDeclaration, "FunctionDeclaration" },
    { TokenType::FunctionCall,        "FunctionCall"        },
    { TokenType::Return,              "Return"              },
    { TokenType::Equals,              "Equals"              },
    { TokenType::Plus,                "Plus"                },
    { TokenType::Minus,               "Minus"               },
    { TokenType::Multiply,            "Multiply"            },
    { TokenType::Divide,              "Divide"              },
    { TokenType::Modulo,              "Modulo"              },
    { TokenType::GreaterThan,         "GreaterThan"         },
    { TokenType::LessThan,            "LessThan"            },
    { TokenType::GreaterThanOrEqual,  "GreaterThanOrEqual"  },
    { TokenType::LessThanOrEqual,     "LessThanOrEqual"     },
    { TokenType::NotEqual,            "NotEqual"            },
    { TokenType::Equal,               "Equal"               },
    { TokenType::Not,                 "Not"                 },
    { TokenType::And,                 "And"                 },
    { TokenType::Or,                  "Or"                  },
    { TokenType::LeftBracket,         "LeftBracket"         },
    { TokenType::RightBracket,        "RightBracket"        },
    { TokenType::LeftCurlyBracket,    "LeftCurlyBracket"    },
    { TokenType::RightCurlyBracket,   "RightCurlyBracket"   },
    { TokenType::EndOfFile,           "EndOfFile"           },
    { TokenType::EndOfLine,           "EndOfLine"           },
    { TokenType::Comment,             "Comment"             },
    { TokenType::Unknown,             "Unknown"             }
};
[[nodiscard]] static inline std::string getTokenTypeAsString(TokenType type) {
    auto it = tokenTypeNames.find(type);
    if (it != tokenTypeNames.end()) {
        return it->second;
    }
    return "Unknown";
    //throw std::runtime_error("Unknown token type");
};
static const std::unordered_map<TokenType, Variables::Type> tokenTypeToVariableType = {
    { TokenType::StringLiteral,  Variables::Type::VT_STRING  },
    { TokenType::IntLiteral,     Variables::Type::VT_INT     },
    { TokenType::DoubleLiteral,  Variables::Type::VT_DOUBLE  },
    { TokenType::BooleanLiteral, Variables::Type::VT_BOOLEAN }
};
static const std::unordered_map<Variables::Type, TokenType> variableTypeToTokenType = {
    { Variables::Type::VT_STRING,  TokenType::StringLiteral  },
    { Variables::Type::VT_INT,     TokenType::IntLiteral     },
    { Variables::Type::VT_DOUBLE,  TokenType::DoubleLiteral  },
    { Variables::Type::VT_BOOLEAN, TokenType::BooleanLiteral }
};
[[nodiscard]] static inline Variables::Type getVariableTypeFromTokenType(TokenType type) {
    auto it = tokenTypeToVariableType.find(type);
    if (it != tokenTypeToVariableType.end()) {
        return it->second;
    }
    return Variables::Type::VT_NOT_DEFINED;
}
[[nodiscard]] static inline std::string getVariableTypeFromTokenTypeAsString(TokenType type) {
    return Variables::TypeToString(getVariableTypeFromTokenType(type));
}
[[nodiscard]] static inline TokenType getTokenTypeFromVariableType(Variables::Type type) {
    auto it = variableTypeToTokenType.find(type);
    if (it != variableTypeToTokenType.end()) {
        return it->second;
    }
    return TokenType::Unknown;
};
[[nodiscard]] static inline TokenType getTokenTypeFromValueDeclaration(const Variables::Type & declaration) {
    if (declaration == Variables::Type::VT_STRING) {
        return TokenType::StringDeclaration;
    }
    if (declaration == Variables::Type::VT_INT) {
        return TokenType::IntDeclaration;
    }
    if (declaration == Variables::Type::VT_DOUBLE) {
        return TokenType::DoubleDeclaration;
    }
    if (declaration == Variables::Type::VT_BOOLEAN) {
        return TokenType::BooleanDeclaration;
    }
    // if (declaration == Variables::Type::VT_FUNCTION) {
    //     return TokenType::FunctionDeclaration;
    // }
    std::cout << "Unknown variable type: " << Variables::TypeToString(declaration) << "\n";
    return TokenType::Unknown;
}
[[nodiscard]] static inline Variables::Type getVariableTypeFromTokenTypeDeclaration(const TokenType & type) {
    if (type == TokenType::StringDeclaration) {
        return Variables::Type::VT_STRING;
    }
    if (type == TokenType::IntDeclaration) {
        return Variables::Type::VT_INT;
    }
    if (type == TokenType::DoubleDeclaration) {
        return Variables::Type::VT_DOUBLE;
    }
    if (type == TokenType::BooleanDeclaration) {
        return Variables::Type::VT_BOOLEAN;
    }
    //if (type == TokenType::FunctionDeclaration) {
    //    return Variables::Type::VT_FUNCTION;
    //}
    return Variables::Type::VT_NULL;
const static std::unordered_map<Tokens::Type, std::string> tokenTypeNames = {
    { Tokens::Type::ParserOpenTag,       "ParserOpenTag"       },
    { Tokens::Type::ParserCloseTag,      "ParserCloseTag"      },
    { Tokens::Type::ParserIfStatement,   "ParserIfStatement"   },
    { Tokens::Type::FileClose,           "FileClose"           },
    { Tokens::Type::Identifier,          "Identifier"          },
    { Tokens::Type::StringLiteral,       "StringLiteral"       },
    { Tokens::Type::IntLiteral,          "IntLiteral"          },
    { Tokens::Type::DoubleLiteral,       "DoubleLiteral"       },
    { Tokens::Type::BooleanLiteral,      "BooleanLiteral"      },
    { Tokens::Type::LeftParenthesis,     "LeftParenthesis"     },
    { Tokens::Type::RightParenthesis,    "RightParenthesis"    },
    { Tokens::Type::Comma,               "Comma"               },
    { Tokens::Type::Semicolon,           "Semicolon"           },
    { Tokens::Type::Variable,            "Variable"            },
    { Tokens::Type::VariableSign,        "VariableSign"        },
    { Tokens::Type::StringDeclaration,   "StringDeclaration"   },
    { Tokens::Type::IntDeclaration,      "IntDeclaration"      },
    { Tokens::Type::DoubleDeclaration,   "DoubleDeclaration"   },
    { Tokens::Type::BooleanDeclaration,  "BooleanDeclaration"  },
    { Tokens::Type::FunctionDeclaration, "FunctionDeclaration" },
    { Tokens::Type::FunctionCall,        "FunctionCall"        },
    { Tokens::Type::Return,              "Return"              },
    { Tokens::Type::Equals,              "Equals"              },
    { Tokens::Type::Plus,                "Plus"                },
    { Tokens::Type::Minus,               "Minus"               },
    { Tokens::Type::Multiply,            "Multiply"            },
    { Tokens::Type::Divide,              "Divide"              },
    { Tokens::Type::Modulo,              "Modulo"              },
    { Tokens::Type::GreaterThan,         "GreaterThan"         },
    { Tokens::Type::LessThan,            "LessThan"            },
    { Tokens::Type::GreaterThanOrEqual,  "GreaterThanOrEqual"  },
    { Tokens::Type::LessThanOrEqual,     "LessThanOrEqual"     },
    { Tokens::Type::NotEqual,            "NotEqual"            },
    { Tokens::Type::Equal,               "Equal"               },
    { Tokens::Type::Not,                 "Not"                 },
    { Tokens::Type::And,                 "And"                 },
    { Tokens::Type::Or,                  "Or"                  },
    { Tokens::Type::LeftBracket,         "LeftBracket"         },
    { Tokens::Type::RightBracket,        "RightBracket"        },
    { Tokens::Type::LeftCurlyBracket,    "LeftCurlyBracket"    },
    { Tokens::Type::RightCurlyBracket,   "RightCurlyBracket"   },
    { Tokens::Type::EndOfFile,           "EndOfFile"           },
    { Tokens::Type::EndOfLine,           "EndOfLine"           },
    { Tokens::Type::Comment,             "Comment"             },
    { Tokens::Type::Unknown,             "Unknown"             }
};
struct TokenPos {
@@ -192,17 +107,14 @@
};
struct Token {
    TokenType       type;
    Tokens::Type type;
    std::string     lexeme;
    std::string     file;
    int             lineNumber;
    size_t          columnNumber;
    TokenPos        pos;
    Variables::Type variableType = Variables::Type::VT_NULL;
    [[nodiscard]] std::string getTypeName() const { return tokenTypeNames.at(type); }
    [[nodiscard]] std::string getVariableTypeName() const { return Variables::TypeToString(variableType); }
    [[nodiscard]] std::string getTokenName() const { return tokenTypeNames.at(type); }
};
}  // namespace Tokens
#endif  // TOKEN_HPP
src/Value.hpp
File was deleted
src/VariableTypes.hpp
File was deleted