From 86904d513734134beffc29c6f4012d53a99f25c5 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Sun, 13 Apr 2025 15:38:34 +0000
Subject: [PATCH] some clean up, added function declaration

---
 src/ScriptExceptionMacros.h      |   40 +
 src/Value.hpp                    |   58 ++
 cmake/options.h.in               |   12 
 src/ScriptInterpreterHelpers.hpp |  101 ++++
 src/Builtins/MathUtilsModule.hpp |   28 +
 src/ScriptInterpreter.cpp        |  222 +++++++---
 test_scripts/test2.vs            |    0 
 src/BaseFunction.hpp             |   68 ++
 CMakeLists.txt                   |   53 +
 src/Builtins/SleepModule.hpp     |   43 +
 src/ScriptInterpreter.hpp        |  115 ++---
 cli/main.cpp                     |   23 
 src/ScriptException.hpp          |  124 +++++
 src/Lexer.hpp                    |   27 
 test_scripts/test1.vs            |    0 
 src/Lexer.cpp                    |  134 ++++--
 src/Builtins/PrintModule.hpp     |   51 -
 src/Token.hpp                    |  192 ++++++-
 src/VariableTypes.hpp            |    7 
 19 files changed, 990 insertions(+), 308 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 95bdf33..d6d4bf2 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -9,32 +9,61 @@
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
 
+set(NEED_CLI ON)
+set(NEED_TESTS OFF)
+set(NEED_STATIC_LIBS ON)
+set(NEED_SHARED_LIBS ON)
 
-option(BUILD_CLI, "Build example commandline intrepeter" ON)
-option(BUILD_TESTS, "Build tests" OFF)
-option(BUILD_SHARED_LIBS "Build shared library" ON)
-option(BUILD_STATIC_LIBS "Build static library" ON)
+option(BUILD_CLI, "Build example commandline intrepeter" ${NEED_CLI})
+option(BUILD_TESTS, "Build tests" ${NEED_TESTS})
+option(BUILD_SHARED_LIBS "Build shared library" ${NEED_SHARED_LIBS})
+option(BUILD_STATIC_LIBS "Build static library" ${NEED_STATIC_LIBS})
+
+if (BUILD_CLI)
+    set(NEED_CLI ${BUILD_CLI})
+endif()
+if (BUILD_TESTS)
+    set(NEED_TESTS ${BUILD_TESTS})
+endif()
+
+if (BUILD_SHARED_LIBS)
+    set(NEED_SHARED_LIBS ${BUILD_SHARED_LIBS})
+endif()
+
+if (BUILD_STATIC_LIBS)
+    set(NEED_STATIC_LIBS ${BUILD_STATIC_LIBS})
+endif()
 
 set(COMMENT_CHARACTER "#")
+set(PARSER_OPEN_TAG "<?void")
+set(PARSER_CLOSE_TAG "?>")
 
-message(STATUS "BUILD_CLI: ${BUILD_CLI}")
-message(STATUS "BUILD_TESTS: ${BUILD_TESTS}")
-message(STATUS "\tCOMMENT_CHARACTER: ${COMMENT_CHARACTER}")
+message(STATUS "BUILD_CLI:           ${NEED_CLI}")
+message(STATUS "BUILD_TESTS:         ${NEED_TESTS}")
+message(STATUS "BUILD_SHARED_LIBS:   ${NEED_SHARED_LIBS}")
+message(STATUS "BUILD_STATIC_LIBS:   ${NEED_STATIC_LIBS}")
+message(STATUS "  COMMENT_CHARACTER: ${COMMENT_CHARACTER}")
+message(STATUS "  PARSER_OPEN_TAG:   ${PARSER_OPEN_TAG}")
+message(STATUS "  PARSER_CLOSE_TAG:  ${PARSER_CLOSE_TAG}")
 
 if (CMAKE_BUILD_TYPE STREQUAL "")
-    set(CMAKE_BUILD_TYPE Release)
-    message(STATUS "CMAKE_BUILD_TYPE is not set, defaulting to Release")
+    set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
 endif()
 
 
 
 message(STATUS "CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}")
+
 if (CMAKE_BUILD_TYPE STREQUAL "Debug")
     set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -g")
     set(DEBUG_BUILD ON)
 endif()
 
 configure_file("cmake/options.h.in" "include/options.h" @ONLY)
+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)
 include_directories(src)
 
@@ -45,12 +74,12 @@
     src/Lexer.cpp
 )
 
-if (BUILD_SHARED_LIBS)
+if (NEED_SHARED_LIBS)
     add_library(${CMAKE_PROJECT_NAME} SHARED)
     target_sources(${CMAKE_PROJECT_NAME} PRIVATE ${SOURCES})
     set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME})
 endif()
-if (BUILD_STATIC_LIBS)
+if (NEED_STATIC_LIBS)
     add_library(${CMAKE_PROJECT_NAME}_static STATIC)
     target_sources(${CMAKE_PROJECT_NAME}_static PRIVATE ${SOURCES})
     set_target_properties(${CMAKE_PROJECT_NAME}_static PROPERTIES OUTPUT_NAME ${CMAKE_PROJECT_NAME})
@@ -59,7 +88,7 @@
 
 set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES LINKER_LANGUAGE CXX)
 
-if (BUILD_CLI)
+if (NEED_CLI)
     add_executable(cli cli/main.cpp)
     add_dependencies(cli ${CMAKE_PROJECT_NAME})
     if (BUILD_SHARED_LIBS)
diff --git a/cli/main.cpp b/cli/main.cpp
index 882134c..1ec1928 100644
--- a/cli/main.cpp
+++ b/cli/main.cpp
@@ -2,6 +2,7 @@
 #include <fstream>
 
 #include "Builtins/PrintModule.hpp"
+#include "Builtins/SleepModule.hpp"
 #include "ScriptInterpreter.hpp"
 
 static bool DEBUG = false;
@@ -15,9 +16,20 @@
     std::string file;
     if (argc == 2) {
         file = argv[1];
-    } else if (argc == 3 && (std::string(argv[1]) == "-d" || std::string(argv[1]) == "--debug")) {
-        DEBUG = true;
-        file  = argv[2];
+    } else if (argc == 3) {
+        if (std::string(argv[1]) == "-d" || std::string(argv[1]) == "--debug") {
+            DEBUG = true;
+            file  = argv[2];
+        } else if (argv[1] == "-h" || argv[1] == "--help") {
+            std::cout << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n";
+            return 0;
+        } else if (argv[1] == "-v" || argv[1] == "--vrsion") {
+            std::cout << "VoidScript v" << VERSION_STRING << "\n";
+            return 0;
+        } else {
+            std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n";
+            return 1;
+        }
     } else {
         std::cerr << "Usage: " << argv[0] << " [-d / --debug] <script_file>\n";
         return 1;
@@ -37,9 +49,10 @@
             return 1;
         }
 
-        std::string        content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
+        std::string       content((std::istreambuf_iterator<char>(input)), std::istreambuf_iterator<char>());
         ScriptInterpreter interp;
-        interp.registerFunction("print", std::make_shared<PrintFunction>());
+        interp.registerModule("print", std::make_shared<PrintFunction>());
+        interp.registerModule("sleep", std::make_shared<SleepFunction>());
         interp.executeScript(content, filename, DEBUG);
     } catch (const std::exception & e) {
         std::cerr << "Parser error: " << e.what() << "\n";
diff --git a/cmake/options.h.in b/cmake/options.h.in
index 384a0f4..c457729 100644
--- a/cmake/options.h.in
+++ b/cmake/options.h.in
@@ -1,3 +1,7 @@
+
+
+#ifndef VOIDSCRIPT_OPTIONS_H
+#define VOIDSCRIPT_OPTIONS_H
 /*
 * THIS IS A GENERATED FILE. DO NOT EDIT.
 */
@@ -7,10 +11,12 @@
 const char EOL = '\n';
 #endif
 const char   COMMENT_CHARACTER = '@COMMENT_CHARACTER@';
-const static char * PARSER_OPEN_TAG   = "<?void";
-const static char * PARSER_CLOSE_TAG  = "?>";
+const static char * PARSER_OPEN_TAG   = "@PARSER_OPEN_TAG@";
+const static char * PARSER_CLOSE_TAG  = "@PARSER_CLOSE_TAG@";
 const static char * VERSION_MINOR     = "@CMAKE_PROJECT_VERSION_MINOR@";
 const static char * VERSION_MAJOR     = "@CMAKE_PROJECT_VERSION_MAJOR@";
 const static char * VERSION_PATCH     = "@CMAKE_PROJECT_VERSION_PATCH@";
 const static char * VERSION_STRING    = "@CMAKE_PROJECT_VERSION@-@CMAKE_BUILD_TYPE@-@CMAKE_SYSTEM@";
-#cmakedefine DEBUG_BUILD
+#cmakedefine01 DEBUG_BUILD
+
+#endif // VOIDSCRIPT_OPTIONS_H
\ No newline at end of file
diff --git a/src/BaseFunction.hpp b/src/BaseFunction.hpp
index a870da6..9038dbc 100644
--- a/src/BaseFunction.hpp
+++ b/src/BaseFunction.hpp
@@ -1,26 +1,78 @@
-// ScriptFunction.hpp
 #ifndef SCRIPT_FUNCTION_HPP
 #define SCRIPT_FUNCTION_HPP
 
+#include <functional>
+#include <stdexcept>
+#include <unordered_map>
+#include <utility>
 #include <vector>
 
+#include "ScriptExceptionMacros.h"
+#include "ScriptInterpreterHelpers.hpp"
 #include "Token.hpp"
 #include "Value.hpp"
 
-
 class ScriptInterpreter;
+
+using CallbackFunction = std::function<Value(const std::vector<Value> &)>;
+using CallBackStorage  = std::unordered_map<std::string, CallbackFunction>;
 
 class BaseFunction {
   protected:
-    std::string name;
+    std::string     name;
+    CallBackStorage functionMap;
+
+
   public:
-    virtual ~BaseFunction()                                                       = default;
-    virtual void  validate(const std::vector<Token> & tokens, size_t & i) const   = 0;
+    BaseFunction(const std::string & functionName) : name(functionName) {}
+
+    virtual void addFunction(const std::string & name, std::function<Value(const std::vector<Value> &)> callback) {
+        functionMap[name] = std::move(callback);
+    }
+
+    virtual void validate(const std::vector<Token> & tokens, size_t & i,
+                          const std::unordered_map<std::string, Value> & variables) {
+        size_t index = i;
+
+        if (tokens[index].type != TokenType::Identifier) {
+            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "identifier");
+        }
+        index++;  // skip function name
+
+        if (tokens[index].type != TokenType::LeftParenthesis) {
+            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "(");
+        }
+        index++;  // skip '('
+
+        std::vector<Token> args;
+        while (tokens[index].type != TokenType::RightParenthesis) {
+            if (tokens[index].type == TokenType::Comma) {
+                index++;
+                continue;
+            }
+            if (tokens[index].type == TokenType::Variable && !variables.contains(tokens[index].lexeme)) {
+                THROW_UNDEFINED_VARIABLE_ERROR(tokens[index].lexeme, tokens[index]);
+            }
+            args.push_back(tokens[index]);
+            index++;
+        }
+
+        index++;  // skip ')'
+
+        if (tokens[index].type != TokenType::Semicolon) {
+            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], ";");
+        }
+
+        this->validateArgs(args, variables);
+        ScriptInterpreterHelpers::expectSemicolon(tokens, index, "function call");
+    }
+
+    virtual void validateArgs(const std::vector<Token> &                     args,
+                              const std::unordered_map<std::string, Value> & variables) = 0;
+
     virtual Value call(const std::vector<Value> & args, bool debug = false) const = 0;
 
-    template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) {
-        FuncClass::registerTo(interp);
-    }
+    template <typename FuncClass> void registerFunctionTo(ScriptInterpreter & interp) { FuncClass::registerTo(interp); }
 };
 
 #endif  // SCRIPT_FUNCTION_HPP
diff --git a/src/Builtins/MathUtilsModule.hpp b/src/Builtins/MathUtilsModule.hpp
new file mode 100644
index 0000000..e85f4d8
--- /dev/null
+++ b/src/Builtins/MathUtilsModule.hpp
@@ -0,0 +1,28 @@
+#ifndef MATH_UTILS_MODULE_HPP
+#define MATH_UTILS_MODULE_HPP
+
+#include <stdexcept>
+#include <vector>
+
+#include "ScriptExceptionMacros.h"
+#include "Value.hpp"
+
+class MathUtils {
+  public:
+    static Value multiply(const std::vector<Value> & args) {
+        if (args.size() != 2) {
+            throw std::runtime_error("multiply expects two arguments.");
+        }
+        if (args[0].type == Variables::Type::VT_INT && args[1].type == Variables::Type::VT_INT) {
+            int  left   = args[0].ToInt();
+            int  right  = args[1].ToInt();
+            auto result = Value();
+            result.data = left * right;
+            result.type = Variables::Type::VT_INT;
+            return result;
+            //return Value::fromInt(left * right);
+        }
+        THROW_INVALID_FUNCTION_ARGUMENT_ERROR("multiply", args[0].TypeToString(), args[0].GetToken());
+    };
+};
+#endif
diff --git a/src/Builtins/PrintModule.hpp b/src/Builtins/PrintModule.hpp
index 0b4ad56..a7bc07d 100644
--- a/src/Builtins/PrintModule.hpp
+++ b/src/Builtins/PrintModule.hpp
@@ -5,54 +5,37 @@
 
 #include "BaseFunction.hpp"
 #include "ScriptExceptionMacros.h"
-#include "ScriptInterpreter.hpp"
 #include "Token.hpp"
 #include "Value.hpp"
 
 class PrintFunction : public BaseFunction {
   private:
-    const std::string name = "print";
+    const std::string name       = "print";
+    bool              addNewLine = false;
   public:
-    void validate(const std::vector<Token> & tokens, size_t & i) const override {
-        auto index = i;
-        if (tokens[index].type != TokenType::Identifier) {
-            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "identifier: " + name);
+    PrintFunction() : BaseFunction(name) {}
+
+    void validateArgs(const std::vector<Token> &                     args,
+                      const std::unordered_map<std::string, Value> & variables) override {
+        if (args.size() == 0) {
+            THROW_UNEXPECTED_TOKEN_ERROR(args[0], "at least one argument");
         }
-        index++;  // skip function name
-        if (tokens[index].type != TokenType::LeftParenthesis) {
-            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "('");
-        }
-        index++;  // skip '('
-        if (tokens[index].type != TokenType::StringLiteral && tokens[index].type != TokenType::Variable &&
-            tokens[index].type != TokenType::IntLiteral && tokens[index].type != TokenType::DoubleLiteral) {
-            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "string, int, double or variable as argument");
-        }
-        size_t count = 0;
-        while (tokens[index].type != TokenType::RightParenthesis) {
-            if (tokens[index].type == TokenType::StringLiteral || tokens[index].type == TokenType::Variable ||
-                tokens[index].type == TokenType::IntLiteral || tokens[index].type == TokenType::DoubleLiteral) {
-                count++;
-                index++;
-            } else if (tokens[index].type == TokenType::Comma) {
-                index++;
-            } else {
-                THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], "string, int, double or variable as argument");
+
+        for (const auto & arg : args) {
+            if (arg.type == TokenType::Variable) {
+                if (!variables.contains(arg.lexeme)) {
+                    THROW_UNDEFINED_VARIABLE_ERROR(arg.lexeme, arg);
+                }
             }
         }
-        if (count == 0) {
-            throw std::runtime_error("print() requires at least one argument at");
-        }
-        index++;  // skip ')'
-        if (tokens[index].type == TokenType::Semicolon) {
-            index++;
-        } else {
-            THROW_UNEXPECTED_TOKEN_ERROR(tokens[index], ";");
+        if (args.end()->variableType == Variables::Type::VT_INT || args.end()->type == TokenType::IntLiteral) {
+            this->addNewLine = true;
         }
     }
 
     Value call(const std::vector<Value> & args, bool debug = false) const override {
         for (const auto & arg : args) {
-            std::cout << arg.ToString();
+            std::cout << arg.ToString(); // todo: add endline if the last parameter is bool
         }
         return Value();
     }
diff --git a/src/Builtins/SleepModule.hpp b/src/Builtins/SleepModule.hpp
new file mode 100644
index 0000000..17585b2
--- /dev/null
+++ b/src/Builtins/SleepModule.hpp
@@ -0,0 +1,43 @@
+#ifndef SLEEPFUNCTION_HPP
+#define SLEEPFUNCTION_HPP
+
+#include <thread>
+
+#include "BaseFunction.hpp"
+
+class SleepFunction : public BaseFunction {
+  public:
+    SleepFunction() : BaseFunction("sleep") {}
+
+    void validateArgs(const std::vector<Token> &                     args,
+                      const std::unordered_map<std::string, Value> & variables) override {
+        if (args.size() != 1) {
+            throw std::runtime_error("sleep() requires exactly one argument");
+        }
+
+        const Token & arg = args[0];
+
+        if (arg.type == TokenType::IntLiteral) {
+            return;
+        }
+
+        if (arg.type == TokenType::Variable) {
+            const auto & value = variables.at(arg.lexeme);
+            if (value.type != Variables::Type::VT_INT) {
+                THROW_VARIABLE_TYPE_MISSMATCH_ERROR(arg.lexeme, Variables::TypeToString(Variables::Type::VT_INT), "",
+                                                    Variables::TypeToString(value.type), arg);
+            }
+            return;
+        }
+
+        THROW_UNEXPECTED_TOKEN_ERROR(arg, "int literal or variable");
+    }
+
+
+    Value call(const std::vector<Value> & args, bool debug = false) const override {
+        std::this_thread::sleep_for(std::chrono::seconds(args[0].ToInt()));
+        return Value();
+    }
+};
+
+#endif  // SLEEPFUNCTION_HPP
diff --git a/src/Lexer.cpp b/src/Lexer.cpp
index 62e110c..c7067d7 100644
--- a/src/Lexer.cpp
+++ b/src/Lexer.cpp
@@ -2,7 +2,7 @@
 
 #include <cctype>
 
-#include "Value.hpp"
+#include "options.h"
 
 Lexer::Lexer(const std::string & source, const std::string & filename) :
     src(source),
@@ -128,17 +128,18 @@
     while (isalpha(peek())) {
         lexeme += advance();
     }
-
-    if (Variables::StringToTypeMap.contains(lexeme)) {
-        const auto type = Variables::StringToTypeMap.at(lexeme);
+    auto it = Variables::StringToTypeMap.find(lexeme);
+    if (it != Variables::StringToTypeMap.end()) {
+        const auto & type = it->second;
         while (isspace(peek())) {
             advance();
         }
         if (peek() == '$') {
-            return variableDeclaration(type);
+            return this->variableDeclaration(type);
         }
         return { TokenType::Identifier, lexeme, filename, lineNumber, startCol };
     }
+
     return { TokenType::Identifier, lexeme, filename, lineNumber, startCol };
 }
 
@@ -151,19 +152,15 @@
         while (isalnum(peek()) || peek() == '_') {
             varName += advance();
         }
-        switch (type) {
-            case Variables::Type::VT_INT:
-                return { TokenType::IntDeclaration, varName, filename, lineNumber, startCol };
-            case Variables::Type::VT_DOUBLE:
-                return { TokenType::DoubleDeclaration, varName, filename, lineNumber, startCol };
-            case Variables::Type::VT_STRING:
-                return { TokenType::StringDeclaration, varName, filename, lineNumber, startCol };
-            default:
-                return { TokenType::Unknown, "Invalid variable type in declaration", filename, lineNumber, startCol };
+        for (auto it = Variables::StringToTypeMap.begin(); it != Variables::StringToTypeMap.end(); ++it) {
+            if (it->second == type) {
+                return { getTokenTypeFromValueDeclaration(it->second), varName, filename, lineNumber, startCol };
+            }
         }
-    } else {
-        return { TokenType::Unknown, "$ followed by invalid character in declaration", filename, lineNumber, startCol };
+
+        return { TokenType::Unknown, "Invalid variable type in declaration", filename, lineNumber, startCol };
     }
+    return { TokenType::Unknown, "$ followed by invalid character in declaration", filename, lineNumber, startCol };
 }
 
 Token Lexer::singleCharToken(TokenType type, const std::string & lexeme) {
@@ -172,15 +169,30 @@
     return { type, lexeme, filename, lineNumber, startCol };
 }
 
-bool Lexer::matchSequence(const std::string & sequence) const {
-    if (pos + sequence.length() > src.length()) {
+bool Lexer::matchSequence(const std::string & sequence, bool caseSensitive) const {
+    if (this->pos + sequence.size() > src.size()) {
         return false;
     }
-    return src.substr(pos, sequence.length()) == sequence;
+
+    for (size_t i = 0; i < sequence.size(); ++i) {
+        char srcChar = src[this->pos + i];
+        char seqChar = sequence[i];
+
+        if (!caseSensitive) {
+            srcChar = std::tolower(static_cast<unsigned char>(srcChar));
+            seqChar = std::tolower(static_cast<unsigned char>(seqChar));
+        }
+
+        if (srcChar != seqChar) {
+            return false;
+        }
+    }
+
+    return true;
 }
 
-void Lexer::matchAndConsume(const std::string & sequence) {
-    if (matchSequence(sequence)) {
+void Lexer::matchAndConsume(const std::string & sequence, bool caseSensitive) {
+    if (matchSequence(sequence, caseSensitive)) {
         for (size_t i = 0; i < sequence.length(); ++i) {
             advance();
         }
@@ -189,18 +201,19 @@
 
 std::vector<Token> Lexer::tokenize() {
     std::vector<Token> tokens;
+    tokens.reserve(src.size() / 4);
 
-    while (!isAtEnd()) {
-        char c = peek();
+    while (pos < src.size()) {
+        char c = src[pos];
         if (isspace(c)) {
             advance();
             continue;
         }
         if (c == '\n') {
-            tokens.push_back(singleCharToken(TokenType::EndOfLine, ""));
+            tokens.push_back(singleCharToken(TokenType::EndOfLine, "\n"));
             continue;
         }
-        if (c == '#') {
+        if (c == COMMENT_CHARACTER) {
             tokens.push_back(comment());
             advance();  // Skip newline after comment
             continue;
@@ -217,27 +230,56 @@
             tokens.push_back({ TokenType::ParserCloseTag, PARSER_CLOSE_TAG, filename, lineNumber, startCol });
             continue;
         }
-        if (isalpha(c)) {
-            tokens.push_back(keywordOrIdentifier());
-        } else if (c == '$') {
-            tokens.push_back(variable());
-        } else if (isdigit(c)) {
-            tokens.push_back(number());
-        } else if (c == '"' || c == '\'') {
-            tokens.push_back(string());
-        } else if (c == '(') {
-            tokens.push_back(singleCharToken(TokenType::LeftParenthesis, "("));
-        } else if (c == ')') {
-            tokens.push_back(singleCharToken(TokenType::RightParenthesis, ")"));
-        } else if (c == ',') {
-            tokens.push_back(singleCharToken(TokenType::Comma, ","));
-        } else if (c == ';') {
-            tokens.push_back(singleCharToken(TokenType::Semicolon, ";"));
-        } else if (c == '=') {
-            tokens.push_back(singleCharToken(TokenType::Equals, "="));
-        } else {
-            tokens.push_back({ TokenType::Unknown, std::string(1, c), filename, lineNumber, colNumber });
-            advance();
+        if (matchSequence("if")) {
+            size_t startCol = colNumber;
+            matchAndConsume("if");
+            tokens.push_back({ TokenType::ParserIfStatement, "if", filename, lineNumber, startCol });
+            continue;
+        }
+
+        switch (c) {
+            case 'a' ... 'z':
+            case 'A' ... 'Z':
+                tokens.push_back(keywordOrIdentifier());
+                break;
+            case '$':
+                tokens.push_back(variable());
+                break;
+            case '0' ... '9':
+                tokens.push_back(number());
+                break;
+            case '"':
+            case '\'':
+                tokens.push_back(string());
+                break;
+            case '(':
+                tokens.push_back(singleCharToken(TokenType::LeftParenthesis, "("));
+                break;
+            case ')':
+                tokens.push_back(singleCharToken(TokenType::RightParenthesis, ")"));
+                break;
+            case ',':
+                tokens.push_back(singleCharToken(TokenType::Comma, ","));
+                break;
+            case ';':
+                tokens.push_back(singleCharToken(TokenType::Semicolon, ";"));
+                break;
+            case '=':
+                tokens.push_back(singleCharToken(TokenType::Equals, "="));
+                break;
+            case '+':
+                tokens.push_back(singleCharToken(TokenType::Plus, "+"));
+                break;
+            case '{':
+                tokens.push_back(singleCharToken(TokenType::LeftCurlyBracket, "{"));
+                break;
+            case '}':
+                tokens.push_back(singleCharToken(TokenType::RightCurlyBracket, "}"));
+                break;
+            default:
+                tokens.push_back({ TokenType::Unknown, std::string(1, c), filename, lineNumber, colNumber });
+                advance();
+                break;
         }
     }
 
diff --git a/src/Lexer.hpp b/src/Lexer.hpp
index b5ac4e3..d374fa1 100644
--- a/src/Lexer.hpp
+++ b/src/Lexer.hpp
@@ -1,13 +1,13 @@
 #ifndef LEXER_HPP
 #define LEXER_HPP
 
+#include <algorithm>
 #include <istream>
 #include <sstream>
 #include <vector>
 
-#include "VariableTypes.hpp"
-#include "options.h"
 #include "Token.hpp"
+#include "VariableTypes.hpp"
 
 class Lexer {
   public:
@@ -32,11 +32,11 @@
     Token variable();
     Token comment();
     Token keywordOrIdentifier();
+    Token boolean();
     Token singleCharToken(TokenType type, const std::string & lexeme);
-    bool matchSequence(const std::string & sequence) const;
+    bool  matchSequence(const std::string & sequence, bool caseSensitive = true) const;
     Token variableDeclaration(Variables::Type type);
-    void matchAndConsume(const std::string & sequence);
-
+    void  matchAndConsume(const std::string & sequence, bool caseSensitive = true);
 
     // validate number types from string
     template <typename Numeric> static bool is_number(const std::string & s) {
@@ -44,7 +44,22 @@
         return ((std::istringstream(s) >> n >> std::ws).eof());
     }
 
-    bool matchSequence(const std::string & sequence) { return src.substr(pos, sequence.length()) == sequence; }
+    bool matchSequence(const std::string & sequence, bool caseSensitive = true) {
+        if (caseSensitive) {
+            return src.substr(pos, sequence.length()) == sequence;
+        }
+
+        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); });
+
+        return srcSubstr == seqLower;
+    }
 };
 
 #endif  // LEXER_HPP
diff --git a/src/ScriptException.hpp b/src/ScriptException.hpp
new file mode 100644
index 0000000..2d7d555
--- /dev/null
+++ b/src/ScriptException.hpp
@@ -0,0 +1,124 @@
+#ifndef SCRIPTEXCEPTION_HPP
+#define SCRIPTEXCEPTION_HPP
+
+#include <stdexcept>
+#include <string>
+
+#include "options.h"
+#include "Token.hpp"
+
+enum class ScriptErrorType : std::uint8_t {
+    UnexpectedToken,
+    UndefinedVariable,
+    UndefinedFunction,
+    VariableTypeMismatch,
+    VariableRedefinition,
+    Custom
+};
+
+class ScriptException : public std::runtime_error {
+  public:
+    ScriptException(ScriptErrorType type, const std::string & message, const std::string & file = "", int line = 0,
+                    const Token & token = Token()) :
+        std::runtime_error(message),
+        type_(type),
+        file_(file),
+        line_(line),
+        token_(token),
+        fullMessage_(formatMessage(message)) {}
+
+    const char * what() const noexcept override { return fullMessage_.c_str(); }
+
+    ScriptErrorType type() const { return type_; }
+
+    const std::string & file() const { return file_; }
+
+    int line() const { return line_; }
+
+    const Token & token() const { return token_; }
+
+    static ScriptException makeUnexpectedTokenError(const Token & token, const std::string & expected = "",
+                                                    const std::string & file = "", int line = 0) {
+        std::string msg = "unexpected token: '" + token.lexeme + "'";
+
+#if DEBUG_BUILD == 1
+        msg.append(" token type: " + tokenTypeNames.at(token.type));
+#endif
+
+        if (!expected.empty()) {
+            msg += ", expected: '" + expected + "'";
+        }
+        return ScriptException(ScriptErrorType::UnexpectedToken, msg, file, line, token);
+    }
+
+    static ScriptException makeUndefinedVariableError(const std::string & name, const Token & token,
+                                                      const std::string & file = "", int line = 0) {
+        std::string msg = "undefined variable: '$" + name + "'";
+        return ScriptException(ScriptErrorType::UndefinedVariable, msg, file, line, token);
+    }
+
+    static ScriptException makeUndefinedFunctionError(const std::string & name, const Token & token,
+                                                      const std::string & file = "", int line = 0) {
+        std::string msg = "undefined function: '" + name + "'";
+#if DEBUG_BUILD == 1
+        msg.append(", type: " + tokenTypeNames.at(token.type));
+#endif
+        return ScriptException(ScriptErrorType::UndefinedFunction, msg, file, line, token);
+    }
+
+    static ScriptException makeVariableRedefinitionError(const std::string & name, const Token & token,
+                                                         const std::string & file = "", int line = 0) {
+        std::string msg = "variable already defined: '" + name + "'";
+        return ScriptException(ScriptErrorType::VariableRedefinition, msg, file, line, token);
+    }
+
+    static ScriptException makeVariableTypeMismatchError(const std::string & targetVar, const std::string & targetType,
+                                                         const std::string & sourceVar, const std::string & sourceType,
+                                                         const Token & token, const std::string & file = "",
+                                                         int line = 0) {
+        std::string msg = "variable type mismatch: '$" + targetVar + "' declared type: '" + targetType + "'";
+        if (!sourceVar.empty()) {
+            msg += ", source variable: '" + sourceVar + "'";
+        }
+        if (!sourceType.empty()) {
+            msg += ", assigned type: '" + sourceType + "'";
+        }
+        return ScriptException(ScriptErrorType::VariableTypeMismatch, msg, file, line, token);
+    }
+
+    static ScriptException makeFunctionRedefinitionError(const std::string & name, const Token & token,
+                                                         const std::string & file = "", int line = 0) {
+        std::string msg = "variable already defined: '" + name + "'";
+        return ScriptException(ScriptErrorType::VariableRedefinition, msg, file, line, token);
+    }
+
+    static ScriptException makeFunctionInvalidArgumentError(const std::string & functionName,
+                                                            const std::string & argName, const Token & token,
+                                                            const std::string & file = "", int line = 0) {
+        std::string msg = "invalid argument for function '" + functionName + "': '" + argName + "'";
+        return ScriptException(ScriptErrorType::Custom, msg, file, line, token);
+    }
+
+  private:
+    ScriptErrorType type_;
+    std::string     file_;
+    int             line_;
+    Token           token_;
+    std::string     fullMessage_;
+
+    std::string formatMessage(const std::string & base) const {
+        std::string formatted = base;
+        if (!token_.file.empty()) {
+            formatted += " in file: " + token_.file + ":" + std::to_string(token_.lineNumber) + ":" +
+                         std::to_string(token_.columnNumber);
+        }
+#if DEBUG_BUILD == 1
+        if (!file_.empty()) {
+            formatted = file_ + ":" + std::to_string(line_) + "\n" + formatted;
+        }
+#endif
+        return formatted;
+    }
+};
+
+#endif  // SCRIPTEXCEPTION_HPP
diff --git a/src/ScriptExceptionMacros.h b/src/ScriptExceptionMacros.h
index 31fa7da..c2e754f 100644
--- a/src/ScriptExceptionMacros.h
+++ b/src/ScriptExceptionMacros.h
@@ -1,19 +1,43 @@
 #ifndef SCRIPT_EXCEPTION_MACROS_H
 #define SCRIPT_EXCEPTION_MACROS_H
 
+#include "ScriptException.hpp"
+
+//
+// Purpose of macros: unified exception handling with extended error information (source file and line number)
+//
+
+// Invalid token type - expected different type
 #define THROW_UNEXPECTED_TOKEN_ERROR(token, expected) \
-    ScriptInterpreter::throwUnexpectedTokenError(token, expected, __FILE__, __LINE__)
+    throw ScriptException::makeUnexpectedTokenError(token, expected, __FILE__, __LINE__)
 
+#define THROW_UNEXPECTED_TOKEN_ERROR_HELPER(token, expected, file, line) \
+    throw ScriptException::makeUnexpectedTokenError(token, expected, file, line)
+
+// Accessing unknown (undefined) variable
 #define THROW_UNDEFINED_VARIABLE_ERROR(name, token) \
-    ScriptInterpreter::throwUndefinedVariableError(name, token, __FILE__, __LINE__)
+    throw ScriptException::makeUndefinedVariableError(name, token, __FILE__, __LINE__)
 
-#define THROW_VARIABLE_TYPE_MISSMATCH_ERROR(target_variable_name, target_variable_type, source_variable_name,       \
-                                            source_variable_type, token)                                            \
-    ScriptInterpreter::throwVariableTypeMissmatchError(target_variable_name, target_variable_type,                  \
-                                                       source_variable_name, source_variable_type, token, __FILE__, \
-                                                       __LINE__)
+// Unknown (undefined) function call
+#define THROW_UNDEFINED_FUNCTION_ERROR(name, token) \
+    throw ScriptException::makeUndefinedFunctionError(name, token, __FILE__, __LINE__)
 
+// Variable type mismatch - e.g. string instead of number
+#define THROW_VARIABLE_TYPE_MISSMATCH_ERROR(target_variable_name, target_variable_type, source_variable_name,         \
+                                            source_variable_type, token)                                              \
+    throw ScriptException::makeVariableTypeMismatchError(target_variable_name, target_variable_type,                  \
+                                                         source_variable_name, source_variable_type, token, __FILE__, \
+                                                         __LINE__)
+
+// Redefining a variable with the same name is not allowed
 #define THROW_VARIABLE_REDEFINITION_ERROR(name, token) \
-    ScriptInterpreter::throwVariableRedefinitionError(name, token, __FILE__, __LINE__)
+    throw ScriptException::makeVariableRedefinitionError(name, token, __FILE__, __LINE__)
+
+#define THROW_FUNCTION_REDEFINITION_ERROR(name, token) \
+    throw ScriptException::makeFunctionRedefinitionError(name, token, __FILE__, __LINE__)
+
+// Invalid or incorrect function argument
+#define THROW_INVALID_FUNCTION_ARGUMENT_ERROR(functionName, argName, token) \
+    throw ScriptException::makeFunctionInvalidArgumentError(functionName, argName, token, __FILE__, __LINE__)
 
 #endif  // SCRIPT_EXCEPTION_MACROS_H
diff --git a/src/ScriptInterpreter.cpp b/src/ScriptInterpreter.cpp
index 28d557c..92e1da6 100644
--- a/src/ScriptInterpreter.cpp
+++ b/src/ScriptInterpreter.cpp
@@ -9,17 +9,17 @@
 #include "ScriptExceptionMacros.h"
 #include "Value.hpp"
 
-void ScriptInterpreter::registerFunction(const std::string & name, std::shared_ptr<BaseFunction> fn) {
+void ScriptInterpreter::registerModule(const std::string & name, std::shared_ptr<BaseFunction> fn) {
     functionObjects[name] = std::move(fn);
 }
 
 Value ScriptInterpreter::evaluateExpression(const Token & token) const {
     if (token.type == TokenType::StringLiteral) {
-        return Value::fromString(token.lexeme);
+        return Value::fromString(token);
     }
     if (token.type == TokenType::IntLiteral) {
         try {
-            return Value::fromInt(std::stoi(token.lexeme));
+            return Value::fromInt(token);
         } catch (const std::invalid_argument & e) {
             throw std::runtime_error("Invalid integer literal: " + token.lexeme);
         } catch (const std::out_of_range & e) {
@@ -28,7 +28,7 @@
     }
     if (token.type == TokenType::DoubleLiteral) {
         try {
-            return Value::fromDouble(std::stod(token.lexeme));
+            return Value::fromDouble(token);
         } catch (const std::invalid_argument & e) {
             throw std::runtime_error("Invalid double literal: " + token.lexeme);
         } catch (const std::out_of_range & e) {
@@ -36,24 +36,22 @@
         }
     }
     if (token.type == TokenType::Variable) {
-        if (variables.find(token.lexeme) != variables.end()) {
-            return variables.at(token.lexeme);
-        }
-        THROW_UNDEFINED_VARIABLE_ERROR(token.lexeme, token);
-    } else {
-        THROW_UNEXPECTED_TOKEN_ERROR(token, "string, integer, double, or variable");
+        return this->getVariable(token);
     }
+    THROW_UNEXPECTED_TOKEN_ERROR(token, "string, integer, double, or variable");
+
     return Value();
 }
 
-std::vector<Value> ScriptInterpreter::parseArguments(const std::vector<Token> & tokens,
-                                                      std::size_t &              current_index) const {
+std::vector<Value> ScriptInterpreter::parseFunctionArguments(const std::vector<Token> & tokens,
+                                                             std::size_t &              index) const {
     std::vector<Value> args;
+    size_t             current_index = index;
 
-    if (current_index >= tokens.size() || tokens[current_index].type != TokenType::Identifier) {
-        THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], tokenTypeNames.at(TokenType::Identifier));
-    }
-    current_index++;  // Skip function name
+    //   if (current_index >= tokens.size() || tokens[current_index].type != TokenType::Identifier) {
+    //       THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], tokenTypeNames.at(TokenType::Identifier));
+    //   }
+    //   current_index++;  // Skip function name
 
     if (current_index >= tokens.size() || tokens[current_index].type != TokenType::LeftParenthesis) {
         THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], tokenTypeNames.at(TokenType::LeftParenthesis));
@@ -77,10 +75,50 @@
         THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], "')'");
     }
     current_index++;  // Skip ')'
-    current_index = current_index;
+    index = current_index;
 
     return args;
 }
+
+void ScriptInterpreter::handleBooleanDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
+    const auto varName = tokens[i].lexeme;
+    const auto varType = tokens[i].variableType;
+    i++;      // Skip variable name
+    if (i < tokens.size() && tokens[i].type == TokenType::Equals) {
+        i++;  // Skip '='
+
+        if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
+            const auto variable = this->getVariable(tokens[i]);
+
+            if (variable.type != Variables::Type::VT_BOOLEAN) {
+                THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_BOOLEAN),
+                                                    tokens[i].lexeme, variable.TypeToString(), tokens[i]);
+            }
+            this->setVariable(varName, variable);
+            i++;  // Skip variable name
+            EXPECT_SEMICOLON(tokens, i, "after bool variable declaration");
+
+        } else if (i < tokens.size() && tokens[i].type == TokenType::Identifier) {
+            std::string lowered = tokens[i].lexeme;
+            std::transform(lowered.begin(), lowered.end(), lowered.begin(),
+                           [](unsigned char c) { return std::tolower(c); });
+
+            if (lowered == "true") {
+                this->setVariable(varName, Value::fromBoolean(tokens[i], true));
+            } else if (lowered == "false") {
+                this->setVariable(varName, Value::fromBoolean(tokens[i], false));
+            } else {
+                THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "true or false after '='");
+            }
+            i++;  // Skip boolean literal
+            EXPECT_SEMICOLON(tokens, i, "after bool declaration");
+        } else {
+            THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "bool literal after '='");
+        }
+    } else {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after bool declaration");
+    }
+};
 
 void ScriptInterpreter::handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
     const auto varName = tokens[i].lexeme;
@@ -91,23 +129,21 @@
         i++;  // Skip '='
 
         if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
-            if (variables.find(tokens[i].lexeme) == variables.end()) {
-                THROW_UNDEFINED_VARIABLE_ERROR(tokens[i].lexeme, tokens[i]);
-            } else {
-                if (variables[tokens[i].lexeme].type != Variables::Type::VT_STRING) {
-                    THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_STRING),
-                                                        tokens[i].lexeme, variables[tokens[i].lexeme].TypeToString(),
-                                                        tokens[i]);
-                }
+            const auto variable = this->getVariable(tokens[i]);
 
-                variables[varName] = variables[tokens[i].lexeme];
-                i++;  // Skip variable name
-                expectSemicolon(tokens, i, "after string variable declaration");
+            if (variable.type != Variables::Type::VT_STRING) {
+                THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_STRING),
+                                                    tokens[i].lexeme, variable.TypeToString(), tokens[i]);
             }
+            this->setVariable(varName, variable);
+            //variables[varName] = variables[tokens[i].lexeme];
+            i++;  // Skip variable name
+            EXPECT_SEMICOLON(tokens, i, "after string variable declaration");
+
         } else if (i < tokens.size() && tokens[i].type == TokenType::StringLiteral) {
-            variables[varName] = Value::fromString(tokens[i].lexeme);
+            this->setVariable(varName, Value::fromString(tokens[i]));
             i++;  // Skip string literal
-            expectSemicolon(tokens, i, "after string declaration");
+            EXPECT_SEMICOLON(tokens, i, "after string declaration");
         } else {
             THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "string literal after '='");
         }
@@ -126,10 +162,12 @@
         if (i < tokens.size()) {
             if (type == TokenType::IntDeclaration && tokens[i].type == TokenType::IntLiteral) {
                 try {
-                    if (variables.find(varName) != variables.end()) {
-                        THROW_VARIABLE_REDEFINITION_ERROR(varName, tokens[i]);
+                    const auto variable = this->getVariable(tokens[i]);
+                    if (variable.type != Variables::Type::VT_INT) {
+                        THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, Variables::TypeToString(Variables::Type::VT_INT),
+                                                            tokens[i].lexeme, variable.TypeToString(), tokens[i]);
                     }
-                    variables[varName] = Value::fromInt(std::stoi(tokens[i].lexeme));
+                    this->setVariable(varName, Value::fromInt(tokens[i]));
                     i++;  // Skip int literal
                 } catch (const std::invalid_argument & e) {
                     throw std::runtime_error("Invalid integer literal in declaration: " + tokens[i].lexeme);
@@ -138,10 +176,13 @@
                 }
             } else if (type == TokenType::DoubleDeclaration && tokens[i].type == TokenType::DoubleLiteral) {
                 try {
-                    if (variables.find(varName) != variables.end()) {
-                        THROW_VARIABLE_REDEFINITION_ERROR(varName, tokens[i]);
+                    const auto variable = this->getVariable(tokens[i]);
+                    if (variable.type != Variables::Type::VT_DOUBLE) {
+                        THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName,
+                                                            Variables::TypeToString(Variables::Type::VT_DOUBLE),
+                                                            tokens[i].lexeme, variable.TypeToString(), tokens[i]);
                     }
-                    variables[varName] = Value::fromDouble(std::stod(tokens[i].lexeme));
+                    this->setVariable(varName, Value::fromDouble(tokens[i]));
                     i++;  // Skip double literal
                 } catch (const std::invalid_argument & e) {
                     throw std::runtime_error("Invalid double literal in declaration: " + tokens[i].lexeme);
@@ -153,7 +194,7 @@
                 THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varName, expectedType, "",
                                                     getVariableTypeFromTokenTypeAsString(tokens[i].type), tokens[i]);
             }
-            expectSemicolon(tokens, i, "after variable declaration");
+            EXPECT_SEMICOLON(tokens, i, "after variable declaration");
         } else {
             THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "literal after '='");
         }
@@ -162,34 +203,87 @@
     }
 }
 
+void ScriptInterpreter::handleFunctionDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
+    const auto varName = tokens[i].lexeme;
+    const auto varType = tokens[i].variableType;
+
+    i++;  // skip funct name
+
+    if (i < tokens.size() && tokens[i].type != TokenType::Equals) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "= after function declaration");
+    }
+    i++;  // skip '='
+
+    if (this->functionParameters.find(varName) != this->functionParameters.end()) {
+        THROW_FUNCTION_REDEFINITION_ERROR(varName, tokens[i]);
+    }
+
+    if (i < tokens.size() && tokens[i].type != TokenType::LeftParenthesis) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "");
+    }
+    i++;
+    // parse arg definitions
+
+    const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i);
+    std::cout << "args: " << args.size() << std::endl;
+    for (const auto & arg : args) {
+        std::cout << "arg name: " << arg.GetToken().lexeme << " type: " << arg.TypeToString() << std::endl;
+    }
+    this->functionParameters[varName].assign(args.begin(), args.end());
+    const std::string body        = ScriptInterpreterHelpers::getFunctionBody(tokens, i);
+    this->functionBodies[varName] = body;
+    // recheck the close curly brace
+    if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
+    }
+    i++;
+    //this->functionBodies[varName] = std::make_shared<ScriptInterpreter>();
+    //this->functionBodies[varName]->executeScript(body, this->filename, true);
+    //EXPECT_SEMICOLON(tokens, i, "after function declaration");
+    // there is no need semicolon to the end of the function declaration
+    std::cout << "function body: \n\"" << body << "\"" << std::endl;
+}
+
 void ScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) {
+    auto        index    = i;
     std::string funcName = tokens[i].lexeme;
-    auto        it       = functionObjects.find(funcName);
+
+    index++;  // skip funct name
+    if (index < tokens.size() && tokens[index].type != TokenType::LeftParenthesis) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[index - 1], "");
+    }
+
+    auto it = functionObjects.find(funcName);
     if (it == functionObjects.end()) {
-        throw std::runtime_error("Unknown function: " + funcName);
+        THROW_UNDEFINED_FUNCTION_ERROR(funcName, tokens[i]);
     }
-    it->second->validate(tokens, i);
-    std::vector<Value> args = parseArguments(tokens, i);
+
+    //    it->second->validate(tokens, i, this->variables);
+
+    std::vector<Value> args = parseFunctionArguments(tokens, index);
     it->second->call(args);
-    if (i < tokens.size() && tokens[i].type == TokenType::Semicolon) {
-        i++;  // Skip ';' after function call
-    }
+    i = index;
+
+    EXPECT_SEMICOLON(tokens, i, "after function call");
+
+    //    if (i < tokens.size() && tokens[i].type == TokenType::Semicolon) {
+    //        i++;  // Skip ';' after function call
+    //    }
 }
 
 void ScriptInterpreter::handleVariableReference(const std::vector<Token> & tokens, std::size_t & i) {
     //THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "function call or variable assignment (not yet implemented)");
-    const auto varName = tokens[i].lexeme;
-    const auto varType = tokens[i].variableType;
+//    const auto varName = tokens[i].lexeme;
+//    const auto varType = tokens[i].variableType;
+    const auto& varToken = tokens[i];
     i++;      // Skip variable token to avoid infinite loop
     if (i < tokens.size() && tokens[i].type == TokenType::Equals) {
         i++;  // Skip '='
         if (i < tokens.size()) {
-            if (variables.find(varName) == variables.end()) {
-                THROW_UNDEFINED_VARIABLE_ERROR(varName, tokens[i]);
-            }
-            variables[varName] = evaluateExpression(tokens[i]);
+            const auto variable = this->getVariable(varToken);
+            this->setVariable(varToken.lexeme, evaluateExpression(tokens[i]));
             i++;  // Skip value
-            expectSemicolon(tokens, i, "after variable assignment");
+            EXPECT_SEMICOLON(tokens, i, "after variable assignment");
         } else {
             THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "value after '='");
         }
@@ -206,16 +300,8 @@
     i++;  // Skip semicolon token
 }
 
-void ScriptInterpreter::expectSemicolon(const std::vector<Token> & tokens, std::size_t & i,
-                                         const std::string & message) const {
-    if (i >= tokens.size() || tokens[i].type != TokenType::Semicolon) {
-        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "; " + message);
-    } else {
-        i++;  // Skip ';'
-    }
-}
-
-void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool debug) {
+void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename, bool ignore_tags) {
+    this->filename = filename;
     Lexer lexer(source, filename);
     auto  tokens = lexer.tokenize();
 
@@ -240,17 +326,21 @@
             continue;
         }
 
-        if (!insideScript) {
-            // Csak kiíratás, ha nem vagyunk script tagben
-            std::cout << token.lexeme;
+        if (insideScript == false && ignore_tags == false) {
+            //std::cout << token.lexeme;
             i++;
             continue;
         }
 
-        // A szokásos feldolgozás csak ha belül vagyunk
         switch (token.type) {
             case TokenType::StringDeclaration:
                 handleStringDeclaration(tokens, i);
+                break;
+            case TokenType::BooleanDeclaration:
+                handleBooleanDeclaration(tokens, i);
+                break;
+            case TokenType::FunctionDeclaration:
+                handleFunctionDeclaration(tokens, i);
                 break;
             case TokenType::IntDeclaration:
             case TokenType::DoubleDeclaration:
@@ -269,7 +359,7 @@
                 handleSemicolon(i);
                 break;
             default:
-                throw std::runtime_error("Unexpected token inside script: " + token.lexeme);
+                THROW_UNEXPECTED_TOKEN_ERROR(token, "");
         }
     }
 }
diff --git a/src/ScriptInterpreter.hpp b/src/ScriptInterpreter.hpp
index 2791e02..5ecc7c3 100644
--- a/src/ScriptInterpreter.hpp
+++ b/src/ScriptInterpreter.hpp
@@ -1,6 +1,7 @@
 #ifndef SSCRIPTINTERPRETER_HPP
 #define SSCRIPTINTERPRETER_HPP
 #include <functional>
+#include <map>
 #include <memory>
 #include <string>
 #include <unordered_map>
@@ -10,90 +11,66 @@
 #include "Token.hpp"
 #include "Value.hpp"
 
-using FunctionValidator = std::function<void(const std::vector<Token> &, size_t &)>;
+using FunctionValidator =
+    std::function<void(const std::vector<Token> &, size_t &, const std::unordered_map<std::string, Value> &)>;
+using VariableContext = std::unordered_map<std::string, Value>;
 
 class ScriptInterpreter {
   public:
-    void registerFunction(const std::string & name, std::shared_ptr<BaseFunction> fn);
-    void executeScript(const std::string & source, const std::string & filenaneame, bool debug = false);
-
-    static void throwUnexpectedTokenError(const Token & token, const std::string & expected = "",
-                                          const std::string & file = "", const int & line = 0) {
-        const std::string error_content =
-            "unexpected token: '" + token.lexeme + "' type: " + tokenTypeNames.at(token.type) +
-            (expected.empty() ? "" : ", expected: '" + expected + "'") + " in file: " + token.file + ":" +
-            std::to_string(token.lineNumber) + ":" + std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
-        const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
-        const std::string& error_message = error_content;
-#endif
-        throw std::runtime_error(error_message);
-    };
-
-    static void throwUndefinedVariableError(const std::string & name, const Token & token, const std::string & file,
-                                            const int & line = 0) {
-        const std::string error_content = "undefined variable: '$" + name + "' in file: " + token.file + ":" +
-                                          std::to_string(token.lineNumber) + ":" + std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
-        const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
-        const std::string& error_message = error_content;
-#endif
-        throw std::runtime_error(error_message);
-    }
-
-    static void throwVariableTypeMissmatchError(const std::string & target_variable_name,
-                                                const std::string & target_type,
-                                                const std::string & source_variable_name,
-                                                const std::string & source_type, const Token & token,
-                                                const std::string & file = "", const int & line = 0) {
-        std::string error_content =
-            "variable type missmatch: '$" + target_variable_name + "' declared type: '" + target_type + "'";
-        if (!source_variable_name.empty()) {
-            error_content += " source variable: '" + source_variable_name + "'";
-        }
-        if (!source_type.empty()) {
-            error_content += " assigned type: '" + source_type + "'";
-        }
-
-        error_content += " in file: " + token.file + ":" + std::to_string(token.lineNumber) + ":" +
-                         std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
-        const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
-        const std::string error_message = error_content;
-#endif
-        throw std::runtime_error(error_message);
-    }
-
-    static void throwVariableRedefinitionError(const std::string & name, const Token & token,
-                                               const std::string & file = "", const int line = 0) {
-        const std::string error_content = "variable alread defined: " + name + " in file: " + token.file + ":" +
-                                          std::to_string(token.lineNumber) + ":" + std::to_string(token.columnNumber);
-#ifdef DEBUG_BUILD
-        const std::string error_message = file + ":" + std::to_string(line) + "\n" + error_content;
-#else
-        const std::string& error_message = error_content;
-#endif
-        throw std::runtime_error(error_message);
-    }
+    void registerModule(const std::string & name, std::shared_ptr<BaseFunction> fn);
+    void executeScript(const std::string & source, const std::string & filenaneame, bool ignore_tags = false);
 
   private:
     std::unordered_map<std::string, FunctionValidator>             functionValidators;
     std::unordered_map<std::string, std::shared_ptr<BaseFunction>> functionObjects;
-    std::unordered_map<std::string, Value>                         variables;
+    // store the current script's variables
+    std::unordered_map<std::string, VariableContext>               variables;
+    // store the current script's function arguments
+    std::unordered_map<std::string, std::vector<Value>>            functionParameters;
+    std::unordered_map<std::string, std::string>                   functionBodies;
+    std::string                                                    filename;
 
-    std::vector<Value> parseArguments(const std::vector<Token> & tokens, std::size_t & current_index) const;
-    Value              evaluateExpression(const Token & token) const;
+    [[nodiscard]] std::vector<Value> parseFunctionArguments(const std::vector<Token> & tokens,
+                                                            std::size_t &              index) const;
+    [[nodiscard]] Value              evaluateExpression(const Token & token) const;
+
     // type handlers
-    void expectSemicolon(const std::vector<Token> & tokens, std::size_t & i, const std::string & message) const;
+    void setVariable(const std::string & name, const Value & value, const std::string & context = "default") {
+        this->variables[context][name] = value;
+    }
+
+    [[nodiscard]] Value getVariable(const std::string & name, const std::string & context = "default") const {
+        for (auto it = variables.begin(); it != variables.end(); ++it) {
+            if (it->first == context) {
+                const auto & innerMap = it->second.find(name);
+                if (innerMap != it->second.end()) {
+                    return it->second.at(name);
+                }
+            }
+        }
+        throw std::runtime_error("Variable not found: " + name);
+    };
+
+    [[nodiscard]] Value getVariable(const Token & token, const std::string & context = "default") const {
+        for (auto it = variables.begin(); it != variables.end(); ++it) {
+            if (it->first == context) {
+                const auto & innerMap = it->second.find(token.lexeme);
+                if (innerMap != it->second.end()) {
+                    return it->second.at(token.lexeme);
+                }
+            }
+        }
+        THROW_UNDEFINED_VARIABLE_ERROR(token.lexeme, token);
+    };
+
     void handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i);
     void handleVariableReference(const std::vector<Token> & tokens, std::size_t & i);
     void handleComment(std::size_t & i);
     void handleSemicolon(std::size_t & i);
     void handleStringDeclaration(const std::vector<Token> & tokens, std::size_t & i);
+    void handleBooleanDeclaration(const std::vector<Token> & tokens, std::size_t & i);
     void handleNumberDeclaration(const std::vector<Token> & tokens, std::size_t & i, TokenType type);
+    void handleFunctionDeclaration(const std::vector<Token> & tokens, std::size_t & i);
 };
 
 #endif  // SSCRIPTINTERPRETER_HPP
diff --git a/src/ScriptInterpreterHelpers.hpp b/src/ScriptInterpreterHelpers.hpp
new file mode 100644
index 0000000..6b615c7
--- /dev/null
+++ b/src/ScriptInterpreterHelpers.hpp
@@ -0,0 +1,101 @@
+#ifndef SCRIPTINTERPRETERHELPERS_HPP
+#define SCRIPTINTERPRETERHELPERS_HPP
+
+#include <iostream>
+#include <ostream>
+#include <vector>
+
+#include "ScriptExceptionMacros.h"
+#include "Token.hpp"
+#include "Value.hpp"
+
+#define EXPECT_SEMICOLON(tokens, i, message) \
+    ScriptInterpreterHelpers::expectSemicolon(tokens, i, message, __FILE__, __LINE__)
+
+namespace ScriptInterpreterHelpers {
+
+static std::string extractSubstring(const std::string & str, size_t start, 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__) {
+    if (i >= tokens.size() || tokens[i].type != TokenType::Semicolon) {
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i - 1], "; " + message, file, line);
+    }
+    i++;  // Skip ';'
+}
+
+[[nodiscard]] static std::vector<Value> parseFunctionDeclarationArguments(const std::vector<Token> & tokens,
+                                                                          std::size_t &              i,
+                                                                          const std::string &        file = __FILE__,
+                                                                          int                        line = __LINE__) {
+    std::vector<Value> arguments;
+
+    // check the arguments types
+    if (tokens[i].type != TokenType::StringDeclaration && tokens[i].type != TokenType::BooleanDeclaration &&
+        tokens[i].type != TokenType::IntDeclaration && tokens[i].type != TokenType::DoubleDeclaration) {
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "variable declaration", file, line);
+    }
+    const auto parameter_type = getVariableTypeFromTokenTypeDeclaration(tokens[i].type);
+    if (parameter_type == Variables::Type::VT_NOT_DEFINED) {
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "valid type identifier", file, line);
+    }
+
+    if (parameter_type == Variables::Type::VT_FUNCTION) {
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "valid type identifier", file, line);
+    }
+
+    if (parameter_type == Variables::Type::VT_NULL) {
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "valid type identifier", file, line);
+    }
+
+    Value val;
+    val.type  = parameter_type;
+    val.token = tokens[i];
+
+    arguments.emplace_back(std::move(val));
+    i++;  // Skip variable declaration
+
+    if (tokens[i].type != TokenType::RightParenthesis) {
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], ") - Only one argument is allowed", file, line);
+    }
+    i++;  // Skip ')'
+
+    return arguments;
+}
+
+[[nodiscard]] static std::string getFunctionBody(const std::vector<Token> & tokens, std::size_t & i) {
+    const size_t first_index = i;
+    if (i >= tokens.size() || tokens[i].type != TokenType::LeftCurlyBracket) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "{");
+    }
+    i++;  // Skip '{'
+    std::string lines;
+
+    while (i < tokens.size() && tokens[i].type != TokenType::RightCurlyBracket) {
+        if (tokens[i].type == TokenType::EndOfLine) {
+            lines += "\n";
+            i++;
+            continue;
+        }
+        if (tokens[i].type == TokenType::EndOfFile) {
+            throw std::runtime_error("Unexpected end of file");
+            break;
+        }
+        lines += tokens[i].lexeme + " ";
+        i++;
+    }
+
+    if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
+    }
+    return lines;
+};
+
+};  // namespace ScriptInterpreterHelpers
+
+#endif  // SCRIPTINTERPRETERHELPERS_HPP
diff --git a/src/Token.hpp b/src/Token.hpp
index b90fa92..1391349 100644
--- a/src/Token.hpp
+++ b/src/Token.hpp
@@ -1,9 +1,6 @@
 #ifndef TOKEN_HPP
 #define TOKEN_HPP
 #include <cstdint>
-#include <iostream>
-#include <ostream>
-#include <stdexcept>
 #include <string>
 #include <unordered_map>
 
@@ -12,55 +9,116 @@
 enum class TokenType : std::uint8_t {
     ParserOpenTag,
     ParserCloseTag,
+    ParserIfStatement,  // if
     FileClose,
     Identifier,
     StringLiteral,
     IntLiteral,
     DoubleLiteral,
-    LeftParenthesis,
-    RightParenthesis,
-    Comma,
-    Semicolon,
-    Variable,           // $variable
-    VariableSign,       // $ variable start sign
-    StringDeclaration,  // string $variable
-    IntDeclaration,     // int $variable
-    DoubleDeclaration,  // double $variable
-    Equals,             // = jel
-    EndOfFile,
-    EndOfLine,
-    Comment,
-    Unknown
+    BooleanLiteral,
+    LeftParenthesis,      // (
+    RightParenthesis,     // )
+    Comma,                // ,
+    Semicolon,            // ;
+    Variable,             // $variable
+    VariableSign,         // $ variable start sign
+    StringDeclaration,    // string $variable
+    IntDeclaration,       // int $variable
+    DoubleDeclaration,    // double $variable
+    BooleanDeclaration,   // bool $variable
+    FunctionDeclaration,  // function $variable
+    Equals,               // =
+    Plus,                 // +
+    Minus,                // -
+    Multiply,             // *
+    Divide,               // /
+    Modulo,               // %
+    GreaterThan,          // >
+    LessThan,             // <
+    GreaterThanOrEqual,   // >=
+    LessThanOrEqual,      // <=
+    NotEqual,             // !=
+    Equal,                // ==
+    Not,                  // !
+    And,                  // &&
+    Or,                   // ||
+    LeftBracket,          // [
+    RightBracket,         // ]
+    LeftCurlyBracket,     // {
+    RightCurlyBracket,    // }
+    EndOfFile,            // \0
+    EndOfLine,            // \n
+    Comment,              // #
+    Unknown               // Unknown
 };
 
 const static std::unordered_map<TokenType, std::string> tokenTypeNames = {
-    { TokenType::ParserOpenTag,     "ParserOpenTag"     },
-    { TokenType::ParserCloseTag,    "ParserCloseTag"    },
-    { TokenType::FileClose,         "FileClose"         },
-    { TokenType::Identifier,        "Identifier"        },
-    { TokenType::StringLiteral,     "StringLiteral"     },
-    { TokenType::IntLiteral,        "IntLiteral"        },
-    { TokenType::DoubleLiteral,     "DoubleLiteral"     },
-    { 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::Equals,            "Equals"            },
-    { TokenType::EndOfFile,         "EndOfFile"         },
-    { TokenType::EndOfLine,         "EndOfLine"         },
-    { TokenType::Comment,           "Comment"           },
-    { TokenType::Unknown,           "Unknown"           }
+    { 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::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::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) {
@@ -76,12 +134,64 @@
     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;
+    }
+    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;
+};
+
+struct TokenPos {
+    size_t start;
+    size_t end;
+};
+
 struct Token {
     TokenType       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); }
diff --git a/src/Value.hpp b/src/Value.hpp
index 2a15758..520e0de 100644
--- a/src/Value.hpp
+++ b/src/Value.hpp
@@ -4,35 +4,77 @@
 #include <string>
 #include <variant>
 
+#include "Token.hpp"
 #include "VariableTypes.hpp"
 
 class Value {
   public:
     Variables::Type          type = Variables::Type::VT_NULL;
     Variables::DataContainer data;
+    Token                    token;
 
     Value() : type(Variables::Type::VT_NULL) {}
 
-    Value(Variables::Type t, double val) : type(t), data(std::move(val)) {}
+    //Value(Variables::Type t, double val) : type(t), data(std::move(val)) {}
 
-    Value(Variables::Type t, int val) : type(t), data(std::move(val)) {}
+    //Value(Variables::Type t, int val) : type(t), data(std::move(val)) {}
 
-    Value(Variables::Type t, const std::string & val) : type(t), data(val) {}
+    //Value(Variables::Type t, const std::string & val) : type(t), data(val) {}
 
-    Value(Variables::Type t, bool val) : type(t), data(std::move(val)) {}
+    //Value(Variables::Type t, bool val) : type(t), data(std::move(val)) {}
 
-    static Value fromInt(int val) { return Value(Variables::Type::VT_INT, val); }
+    Value(Variables::Type variable_type, const Token & token) : type(variable_type), token(token) {
+        if (type == Variables::Type::VT_INT) {
+            data = std::stoi(token.lexeme);
+        } else if (type == Variables::Type::VT_DOUBLE) {
+            data = std::stod(token.lexeme);
+        } else if (type == Variables::Type::VT_BOOLEAN) {
+            data = token.lexeme == "true";
+        } else if (type == Variables::Type::VT_NULL) {
+            data = Variables::TypeToString(type);
+        } else if (type == Variables::Type::VT_NOT_DEFINED) {
+            data = Variables::TypeToString(type);
+        } else {
+            data = token.lexeme;
+        }
+    }
 
-    static Value fromDouble(double val) { return Value(Variables::Type::VT_DOUBLE, val); }
+    //Value(const Token & token) : token(token), type(token.variableType), data(token.lexeme) {}
 
-    static Value fromString(const std::string & val) { return { Variables::Type::VT_STRING, val }; }
+    //static Value fromInt(int val) { return Value(Variables::Type::VT_INT, val); }
 
-    static Value fromBoolean(bool val) { return { Variables::Type::VT_BOOLEAN, val }; }
+    //static Value fromDouble(double val) { return Value(Variables::Type::VT_DOUBLE, val); }
+
+    //static Value fromString(const std::string & val) { return { Variables::Type::VT_STRING, val }; }
+
+    //static Value fromBoolean(bool val) { return { Variables::Type::VT_BOOLEAN, val }; }
+
+    static Value fromInt(const Token & token) { return Value(Variables::Type::VT_INT, token); }
+
+    static Value fromDouble(const Token & token) { return Value(Variables::Type::VT_DOUBLE, token); }
+
+    static Value fromString(const Token & token) { return Value(Variables::Type::VT_STRING, token); }
+
+    static Value fromBoolean(const Token & token, bool state) {
+        auto result = Value(Variables::Type::VT_BOOLEAN, token);
+        result.data = state;
+        return result;
+    }
 
     std::string ToString() const { return decodeEscapes(Variables::ToString(data, type)); }
 
+    int ToInt() const { return std::get<int>(data); }
+
+    double ToDouble() const { return std::get<double>(data); }
+
+    bool ToBool() const { return std::get<bool>(data); }
+
     std::string TypeToString() const { return Variables::TypeToString(type); }
 
+    void SetToken(const Token & token) { this->token = token; }
+
+    const Token & GetToken() const { return token; }
+
   private:
     Value(Variables::Type t, std::variant<int, double, std::string, bool> && val) : type(t), data(std::move(val)) {}
 
diff --git a/src/VariableTypes.hpp b/src/VariableTypes.hpp
index 6a3d5f1..e03c242 100644
--- a/src/VariableTypes.hpp
+++ b/src/VariableTypes.hpp
@@ -10,22 +10,25 @@
 
 using DataContainer = std::variant<int, double, std::string, bool>;
 
-enum class Type : std::uint8_t { VT_INT, VT_DOUBLE, VT_STRING, VT_BOOLEAN, VT_NULL, VT_NOT_DEFINED };
+enum class Type : std::uint8_t { VT_INT, VT_DOUBLE, VT_STRING, VT_BOOLEAN, VT_NULL, VT_FUNCTION, VT_NOT_DEFINED };
 
 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        },
+    { "function",    Type::VT_FUNCTION    },
     { "not_defined", Type::VT_NOT_DEFINED },
 };
 const std::unordered_map<Type, std::string> StypeToStringMap = {
     { Type::VT_INT,         "int"         },
     { Type::VT_DOUBLE,      "double"      },
     { Type::VT_STRING,      "string"      },
-    { Type::VT_BOOLEAN,     "boolean"     },
+    { Type::VT_BOOLEAN,     "bool"        },
     { Type::VT_NULL,        "null"        },
+    { Type::VT_FUNCTION,    "function"    },
     { Type::VT_NOT_DEFINED, "not_defined" },
 };
 
diff --git a/test_scripts/test1.ss b/test_scripts/test1.vs
similarity index 100%
rename from test_scripts/test1.ss
rename to test_scripts/test1.vs
diff --git a/test_scripts/test2.ss b/test_scripts/test2.vs
similarity index 100%
rename from test_scripts/test2.ss
rename to test_scripts/test2.vs

--
Gitblit v1.9.3