From c34b2c57219aa496a202c2be1e12332b4eeea440 Mon Sep 17 00:00:00 2001
From: Ferenc Szontágh <szf@fsociety.hu>
Date: Mon, 14 Apr 2025 15:43:20 +0000
Subject: [PATCH] add function parameter handling and contextes

---
 src/ScriptExceptionMacros.h      |    9 +
 src/Value.hpp                    |    2 
 cmake/options.h.in               |    6 
 src/ScriptInterpreterHelpers.hpp |   17 +-
 src/ScriptInterpreter.cpp        |  133 ++++++++++++++--------
 CMakeLists.txt                   |   22 ++-
 src/ScriptInterpreter.hpp        |   31 ++++-
 cli/main.cpp                     |    2 
 src/ScriptException.hpp          |   28 ++++
 src/Lexer.hpp                    |    1 
 src/Lexer.cpp                    |   46 ++++++-
 src/Token.hpp                    |   20 ++-
 src/VariableTypes.hpp            |    4 
 13 files changed, 225 insertions(+), 96 deletions(-)

diff --git a/CMakeLists.txt b/CMakeLists.txt
index 14318e8..29ae97b 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -23,16 +23,24 @@
 
 include(cmake/AppVersion.cmake)
 
-set(COMMENT_CHARACTER "#")
+set(IDENTIFIER_COMMENT "#")
 set(PARSER_OPEN_TAG "<?void")
 set(PARSER_CLOSE_TAG "?>")
+set(IDENTIFIER_FUNCTION "function")
+set(IDENTIFIER_VARIABLE "$")
+set(IDENTIFIER_IF "if")
+set(IDENTIFIER_RETURN "return")
 
-message(STATUS "BUILD_CLI:           ${NEED_CLI}")
-message(STATUS "BUILD_SHARED_LIBS:   ${BUILD_SHARED_LIBS}")
-message(STATUS "  COMMENT_CHARACTER: ${COMMENT_CHARACTER}")
-message(STATUS "  PARSER_OPEN_TAG:   ${PARSER_OPEN_TAG}")
-message(STATUS "  PARSER_CLOSE_TAG:  ${PARSER_CLOSE_TAG}")
-message(STATUS "APP_GIT_VERSION:     ${APP_GIT_VERSION}")
+message(STATUS "BUILD_CLI:             ${NEED_CLI}")
+message(STATUS "BUILD_SHARED_LIBS:     ${BUILD_SHARED_LIBS}")
+message(STATUS "  IDENTIFIER_COMMENT:  ${IDENTIFIER_COMMENT}")
+message(STATUS "  IDENTIFIER_FUNCTION: ${IDENTIFIER_FUNCTION}")
+message(STATUS "  IDENTIFIER_VARIABLE: ${IDENTIFIER_VARIABLE}")
+message(STATUS "  IDENTIFIER_IF:       ${IDENTIFIER_IF}")
+message(STATUS "  IDENTIFIER_RETURN:   ${IDENTIFIER_RETURN}")
+message(STATUS "  PARSER_OPEN_TAG:     ${PARSER_OPEN_TAG}")
+message(STATUS "  PARSER_CLOSE_TAG:    ${PARSER_CLOSE_TAG}")
+message(STATUS "APP_GIT_VERSION:       ${APP_GIT_VERSION}")
 
 if (CMAKE_BUILD_TYPE STREQUAL "")
     set(CMAKE_BUILD_TYPE Release CACHE STRING "Build type" FORCE)
diff --git a/cli/main.cpp b/cli/main.cpp
index 67bfb52..a20c2bc 100644
--- a/cli/main.cpp
+++ b/cli/main.cpp
@@ -66,7 +66,7 @@
         ScriptInterpreter interp;
         interp.registerModule("print", std::make_shared<PrintFunction>());
         interp.registerModule("sleep", std::make_shared<SleepFunction>());
-        interp.executeScript(content, filename, "_default_", false);
+        interp.executeScript(content, filename, "DEFAULT", false);
     } catch (const std::exception & e) {
         std::cerr << "Parser error: " << e.what() << "\n";
         return 1;
diff --git a/cmake/options.h.in b/cmake/options.h.in
index 7fb70bd..364d98d 100644
--- a/cmake/options.h.in
+++ b/cmake/options.h.in
@@ -10,9 +10,13 @@
 #else
 const char EOL = '\n';
 #endif
-const char          COMMENT_CHARACTER   = '@COMMENT_CHARACTER@';
+const static char   IDENTIFIER_COMMENT   = '@IDENTIFIER_COMMENT@';
 const static char * PARSER_OPEN_TAG     = "@PARSER_OPEN_TAG@";
 const static char * PARSER_CLOSE_TAG    = "@PARSER_CLOSE_TAG@";
+const static char   IDENTIFIER_VARIABLE = '@IDENTIFIER_VARIABLE@';
+const static char * IDENTIFIER_FUNCTION = "@IDENTIFIER_FUNCTION@";
+const static char * IDENTIFIER_RETURN   = "@IDENTIFIER_RETURN@";
+const static char * IDENTIFIER_IF       = "@IDENTIFIER_IF@";
 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@";
diff --git a/src/Lexer.cpp b/src/Lexer.cpp
index 6d003a3..441ee40 100644
--- a/src/Lexer.cpp
+++ b/src/Lexer.cpp
@@ -12,6 +12,11 @@
     colNumber(1),
     charNumber(0) {}
 
+/**
+ * Peek at the current character without advancing the lexer's position.
+ *
+ * @return The current character, or '\0' if at the end of the source.
+ */
 char Lexer::peek() const {
     return pos < src.size() ? src[pos] : '\0';
 }
@@ -177,18 +182,47 @@
     while (isalpha(peek())) {
         lexeme += advance();
     }
+    if (lexeme == IDENTIFIER_FUNCTION) {
+        return this->functionDeclarationToken();
+    }
+
+    if (lexeme == IDENTIFIER_RETURN) {
+        return createToken(TokenType::Return, lexeme);
+    }
+    if (lexeme == IDENTIFIER_IF) {
+        return createToken(TokenType::ParserIfStatement, lexeme);
+    }
+
+    if (peek() == '(') {  // Function call
+        return createToken(TokenType::FunctionCall, lexeme);
+    }
+
     auto it = Variables::StringToTypeMap.find(lexeme);
     if (it != Variables::StringToTypeMap.end()) {
         const auto & type = it->second;
         while (isspace(peek())) {
             advance();
         }
-        if (peek() == '$') {
+
+        if (peek() == IDENTIFIER_VARIABLE) {
             return this->variableDeclarationToken(type);
         }
         return createToken(TokenType::Identifier, lexeme);
     }
     return createToken(TokenType::Identifier, lexeme);
+}
+
+Token Lexer::functionDeclarationToken() {
+    advance();  // Skip function
+    std::string functionName;
+    if (isalpha(peek()) || peek() == '_') {
+        functionName += advance();
+        while (isalnum(peek()) || peek() == '_') {
+            functionName += advance();
+        }
+        return createToken(TokenType::FunctionDeclaration, functionName);
+    }
+    return createUnknownToken("function followed by invalid character");
 }
 
 Token Lexer::variableDeclarationToken(Variables::Type type) {
@@ -250,7 +284,7 @@
             tokens.push_back(createSingleCharToken(TokenType::EndOfLine, "\n"));
             continue;
         }
-        if (c == COMMENT_CHARACTER) {
+        if (c == IDENTIFIER_COMMENT) {
             tokens.push_back(commentToken());
             advance();  // Skip newline after comment
             continue;
@@ -265,18 +299,12 @@
             tokens.push_back(createToken(TokenType::ParserCloseTag, PARSER_CLOSE_TAG));
             continue;
         }
-        if (matchSequence("if")) {
-            matchAndConsume("if");
-            tokens.push_back(createToken(TokenType::ParserIfStatement, "if"));
-            continue;
-        }
-
         switch (c) {
             case 'a' ... 'z':
             case 'A' ... 'Z':
                 tokens.push_back(keywordOrIdentifierToken());
                 break;
-            case '$':
+            case IDENTIFIER_VARIABLE:
                 tokens.push_back(variableToken());
                 break;
             case '0' ... '9':
diff --git a/src/Lexer.hpp b/src/Lexer.hpp
index 5648ab7..1207dd9 100644
--- a/src/Lexer.hpp
+++ b/src/Lexer.hpp
@@ -49,6 +49,7 @@
     Token variableToken();
     Token commentToken();
     Token keywordOrIdentifierToken();
+    Token functionDeclarationToken();
     Token variableDeclarationToken(Variables::Type type);
 
     // validate number types from string
diff --git a/src/ScriptException.hpp b/src/ScriptException.hpp
index 2d7d555..d1f88c2 100644
--- a/src/ScriptException.hpp
+++ b/src/ScriptException.hpp
@@ -37,6 +37,15 @@
 
     const Token & token() const { return token_; }
 
+    static ScriptException makeUnexpectedEndOfFileError(const Token & token, const std::string & file = "",
+                                                        int line = 0) {
+        std::string msg = "unexpected end of file";
+        if (!token.lexeme.empty()) {
+            msg += " near '" + token.lexeme + "'";
+        }
+        return ScriptException(ScriptErrorType::UnexpectedToken, msg, file, line, 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 + "'";
@@ -46,7 +55,7 @@
 #endif
 
         if (!expected.empty()) {
-            msg += ", expected: '" + expected + "'";
+            msg += ", expected " + expected;
         }
         return ScriptException(ScriptErrorType::UnexpectedToken, msg, file, line, token);
     }
@@ -61,7 +70,7 @@
                                                       const std::string & file = "", int line = 0) {
         std::string msg = "undefined function: '" + name + "'";
 #if DEBUG_BUILD == 1
-        msg.append(", type: " + tokenTypeNames.at(token.type));
+        msg.append(", type: " + getTokenTypeAsString(token.type));
 #endif
         return ScriptException(ScriptErrorType::UndefinedFunction, msg, file, line, token);
     }
@@ -99,6 +108,21 @@
         return ScriptException(ScriptErrorType::Custom, msg, file, line, token);
     }
 
+    static ScriptException makeFunctionArgumentCountMismatchError(const std::string & functionName,
+                                                                  const size_t & expected, size_t actual,
+                                                                  const Token & token, const std::string & file = "",
+                                                                  int line = 0) {
+        std::string msg = "invalid argument count for function '" + functionName + "', expected " +
+                          std::to_string(expected) + ", got " + std::to_string(actual);
+        return ScriptException(ScriptErrorType::Custom, msg, file, line, token);
+    }
+
+    static ScriptException makeFunctionBodyEmptyError(const std::string & functionName, const Token & token,
+                                                      const std::string & file = "", int line = 0) {
+        std::string msg = "function '" + functionName + "' has no body";
+        return ScriptException(ScriptErrorType::Custom, msg, file, line, token);
+    }
+
   private:
     ScriptErrorType type_;
     std::string     file_;
diff --git a/src/ScriptExceptionMacros.h b/src/ScriptExceptionMacros.h
index 55dce1c..d0263ed 100644
--- a/src/ScriptExceptionMacros.h
+++ b/src/ScriptExceptionMacros.h
@@ -6,6 +6,8 @@
 //
 // Purpose of macros: unified exception handling with extended error information (source file and line number)
 //
+#define THROW_UNEXPECTED_END_OF_FILE_ERROR(token) \
+    throw ScriptException::makeUnexpectedEndOfFileError(token, __FILE__, __LINE__)
 
 // Invalid token type - expected different type
 #define THROW_UNEXPECTED_TOKEN_ERROR(token, expected) \
@@ -43,4 +45,11 @@
 #define THROW_INVALID_FUNCTION_ARGUMENT_ERROR(functionName, argName, token) \
     throw ScriptException::makeFunctionInvalidArgumentError(functionName, argName, token, __FILE__, __LINE__)
 
+#define THROW_FUNCTION_ARG_COUNT_MISMATCH_ERROR(functionName, expected, actual, token)                             \
+    throw ScriptException::makeFunctionArgumentCountMismatchError(functionName, expected, actual, token, __FILE__, \
+                                                                  __LINE__)
+
+#define THROW_FUNCTION_BODY_EMPTY(funcName, token) \
+    throw ScriptException::makeFunctionBodyEmptyError(funcName, token, __FILE__, __LINE__)
+
 #endif  // SCRIPT_EXCEPTION_MACROS_H
diff --git a/src/ScriptInterpreter.cpp b/src/ScriptInterpreter.cpp
index ee7cf20..1f353cf 100644
--- a/src/ScriptInterpreter.cpp
+++ b/src/ScriptInterpreter.cpp
@@ -54,11 +54,6 @@
     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::LeftParenthesis) {
         THROW_UNEXPECTED_TOKEN_ERROR(tokens[current_index], tokenTypeNames.at(TokenType::LeftParenthesis));
     }
@@ -93,7 +88,7 @@
         i++;  // Skip '='
 
         if (i < tokens.size() && tokens[i].type == TokenType::Variable) {
-            const auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
+            auto variable = this->getVariable(tokens[i], this->contextPrefix, __FILE__, __LINE__);
 
             if (variable.type != Variables::Type::VT_BOOLEAN) {
                 THROW_VARIABLE_TYPE_MISSMATCH_ERROR(varToken.lexeme,
@@ -111,9 +106,11 @@
                            [](unsigned char c) { return std::tolower(c); });
 
             if (lowered == "true") {
-                this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], true), this->contextPrefix, true);
+                auto value = Value::fromBoolean(tokens[i], true);
+                this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
             } else if (lowered == "false") {
-                this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], false), this->contextPrefix, true);
+                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 '='");
             }
@@ -122,9 +119,11 @@
         } else if (i < tokens.size() && tokens[i].type == TokenType::IntLiteral) {
             const auto test = std::stoi(tokens[i].lexeme);
             if (test == 0) {
-                this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], false), this->contextPrefix, true);
+                auto value = Value::fromBoolean(tokens[i], false);
+                this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
             } else if (test > 0) {
-                this->setVariable(varToken.lexeme, Value::fromBoolean(tokens[i], true), this->contextPrefix, true);
+                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 '='");
             }
@@ -154,12 +153,14 @@
                                                     Variables::TypeToString(Variables::Type::VT_STRING),
                                                     tokens[i].lexeme, variable.TypeToString(), tokens[i]);
             }
-            this->setVariable(varToken.lexeme, variable, this->contextPrefix, true);
+            auto value = variable;
+            this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
             i++;  // Skip variable name
             EXPECT_SEMICOLON(tokens, i, "after string variable declaration");
 
         } else if (i < tokens.size() && tokens[i].type == TokenType::StringLiteral) {
-            this->setVariable(varToken.lexeme, Value::fromString(tokens[i]), this->contextPrefix, true);
+            auto value = Value::fromString(tokens[i]);
+            this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
             i++;  // Skip string literal
             EXPECT_SEMICOLON(tokens, i, "after string declaration");
         } else {
@@ -179,7 +180,8 @@
         if (i < tokens.size()) {
             if (type == TokenType::IntDeclaration && tokens[i].type == TokenType::IntLiteral) {
                 try {
-                    this->setVariable(varToken.lexeme, Value::fromInt(tokens[i]), this->contextPrefix, true);
+                    auto value = Value::fromInt(tokens[i]);
+                    this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
                     i++;  // Skip int literal
                 } catch (const std::invalid_argument & e) {
                     throw std::runtime_error("Invalid integer literal in declaration: " + tokens[i].lexeme);
@@ -188,7 +190,8 @@
                 }
             } else if (type == TokenType::DoubleDeclaration && tokens[i].type == TokenType::DoubleLiteral) {
                 try {
-                    this->setVariable(varToken.lexeme, Value::fromDouble(tokens[i]), this->contextPrefix, true);
+                    auto value = Value::fromDouble(tokens[i]);
+                    this->setVariable(varToken.lexeme, value, this->contextPrefix, true);
                     i++;  // Skip double literal
                 } catch (const std::invalid_argument & e) {
                     throw std::runtime_error("Invalid double literal in declaration: " + tokens[i].lexeme);
@@ -210,7 +213,7 @@
 }
 
 void ScriptInterpreter::handleFunctionDeclaration(const std::vector<Token> & tokens, std::size_t & i) {
-    const auto varName = tokens[i].lexeme;
+    const auto & funcToken = tokens[i];
 
     i++;  // skip funct name
 
@@ -219,8 +222,8 @@
     }
     i++;  // skip '='
 
-    if (this->functionParameters.find(varName) != this->functionParameters.end()) {
-        THROW_FUNCTION_REDEFINITION_ERROR(varName, tokens[i]);
+    if (this->functionBodies.find(funcToken.lexeme) != this->functionBodies.end()) {
+        THROW_FUNCTION_REDEFINITION_ERROR(funcToken.lexeme, tokens[i]);
     }
 
     if (i < tokens.size() && tokens[i].type != TokenType::LeftParenthesis) {
@@ -228,54 +231,67 @@
     }
     i++;
     // parse arg definitions
-
-    const auto args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i, __FILE__, __LINE__);
-    std::cout << "args: " << args.size() << '\n';
+    const std::string context_name = this->getContextName(funcToken.lexeme);
+    const auto        args = ScriptInterpreterHelpers::parseFunctionDeclarationArguments(tokens, i, __FILE__, __LINE__);
     for (const auto & arg : args) {
-        std::cout << "arg name: " << arg.GetToken().lexeme << " type: " << arg.TypeToString() << '\n';
+        auto value = arg;
+        this->setVariable(arg.GetToken().lexeme, value, context_name, true);
     }
-    this->functionParameters[varName].assign(args.begin(), args.end());
+
     size_t start;
     size_t end;
     ScriptInterpreterHelpers::getFunctionBody(tokens, i, start, end);
-    std::cout << "Body start:  " << start << " end: " << end << '\n';
+
     const std::string function_body = ScriptInterpreterHelpers::extractSubstring(this->source, start, end);
-    this->functionBodies[varName]   = function_body;
+    if (function_body.empty()) {
+        std::cout << this->source << '\n';
+        THROW_FUNCTION_BODY_EMPTY(funcToken.lexeme, tokens[i - 1]);
+    }
+    this->functionBodies[funcToken.lexeme] = function_body;
     // recheck the close curly brace
     if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
         THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
     }
     i++;
-#if DEBUG_BUILD == 1
-    std::cout << "function body: \n\"" << function_body << "\"" << '\n';
-#endif
 }
 
 void ScriptInterpreter::handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i) {
-    auto        index    = i;
-    std::string funcName = tokens[i].lexeme;
+    const auto & functiontoken = tokens[i];
 
-    index++;  // skip funct name
-    if (index < tokens.size() && tokens[index].type != TokenType::LeftParenthesis) {
-        THROW_UNEXPECTED_TOKEN_ERROR(tokens[index - 1], "");
+    i++;  // skip funct name
+    if (i < tokens.size() && tokens[i].type != TokenType::LeftParenthesis) {
+        THROW_UNEXPECTED_TOKEN_ERROR(tokens[i - 1], "");
     }
 
-    auto it = functionObjects.find(funcName);
-    if (it == functionObjects.end()) {
-        THROW_UNDEFINED_FUNCTION_ERROR(funcName, tokens[i]);
+    std::vector<Value> args = parseFunctionArguments(tokens, i);
+
+    auto it1 = functionObjects.find(functiontoken.lexeme);
+    if (it1 != functionObjects.end()) {
+        it1->second->call(args);
+    } else {
+        auto it2 = functionBodies.find(functiontoken.lexeme);
+        if (it2 == functionBodies.end()) {
+            THROW_UNDEFINED_FUNCTION_ERROR(functiontoken.lexeme, tokens[i]);
+        }
+
+        if (args.size() > 0) {
+            auto varList = this->getcontextVariables(this->getContextName(functiontoken.lexeme));
+            if (varList.size() != args.size()) {
+                THROW_FUNCTION_ARG_COUNT_MISMATCH_ERROR(functiontoken.lexeme, varList.size(), args.size(), tokens[i]);
+            }
+            size_t counter = 0;
+            for (auto & var : varList) {
+                this->setVariable(var.first, args[counter], var.second.context, false, true);
+                counter++;
+            }
+        }
+
+        this->executeScript(it2->second, this->filename, functiontoken.lexeme, true);
     }
 
-    //    it->second->validate(tokens, i, this->variables);
-
-    std::vector<Value> args = parseFunctionArguments(tokens, index);
-    it->second->call(args);
-    i = index;
+    //i++;
 
     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) {
@@ -285,7 +301,8 @@
         i++;  // Skip '='
         if (i < tokens.size()) {
             const auto variable = this->getVariable(varToken, this->contextPrefix, __FILE__, __LINE__);
-            this->setVariable(varToken.lexeme, evaluateExpression(tokens[i]), this->contextPrefix, false);
+            auto       value    = evaluateExpression(tokens[i]);
+            this->setVariable(varToken.lexeme, value, this->contextPrefix, false);
             i++;  // Skip value
             EXPECT_SEMICOLON(tokens, i, "after variable assignment");
         } else {
@@ -298,9 +315,19 @@
 
 void ScriptInterpreter::executeScript(const std::string & source, const std::string & filename,
                                       const std::string & _namespace, bool ignore_tags) {
-    this->filename      = filename;
-    this->source        = source;
-    this->contextPrefix = filename + _namespace;
+    std::string oldContext;
+    std::string oldSource;
+
+    this->filename = filename;
+    if (!this->source.empty()) {
+        oldSource = this->source;
+    }
+    this->source = source;
+
+    if (!this->contextPrefix.empty()) {
+        oldContext = this->contextPrefix;
+    }
+    this->contextPrefix = this->getContextName(_namespace);
     Lexer lexer(source, filename);
     auto  tokens = lexer.tokenize();
 
@@ -345,7 +372,7 @@
             case TokenType::DoubleDeclaration:
                 handleNumberDeclaration(tokens, i, token.type);
                 break;
-            case TokenType::Identifier:
+            case TokenType::FunctionCall:
                 handleFunctionCall(tokens, i);
                 break;
             case TokenType::Variable:
@@ -361,4 +388,12 @@
                 THROW_UNEXPECTED_TOKEN_ERROR(token, "");
         }
     }
+    if (!oldContext.empty()) {
+        this->contextPrefix = oldContext;
+        oldContext.clear();
+    }
+    if (!oldSource.empty()) {
+        this->source = oldSource;
+        oldSource.clear();
+    }
 }
diff --git a/src/ScriptInterpreter.hpp b/src/ScriptInterpreter.hpp
index 50bdfa9..b8077bd 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>
@@ -12,13 +13,13 @@
 
 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>;
+using VariableContext = std::map<std::string, Value>;
 
 class ScriptInterpreter {
   public:
     void registerModule(const std::string & name, std::shared_ptr<BaseFunction> fn);
     void executeScript(const std::string & source, const std::string & filename,
-                       const std::string & _namespace = "_default_", bool ignore_tags = false);
+                       const std::string & _namespace = "DEFAULT", bool ignore_tags = false);
 
   private:
     std::unordered_map<std::string, FunctionValidator>             functionValidators;
@@ -37,11 +38,16 @@
     [[nodiscard]] Value              evaluateExpression(const Token & token) const;
 
     // type handlers
-    void setVariable(const std::string & name, const Value & value, const std::string & context = "default",
-                     bool exception_if_exists = false) {
+    void setVariable(const std::string & name, Value & value, const std::string & context = "default",
+                     bool exception_if_exists = false, bool exception_if_not_exists = false) {
         if (exception_if_exists && variables[context].find(name) != variables[context].end()) {
             THROW_VARIABLE_REDEFINITION_ERROR(name, value.token);
         }
+        if (exception_if_not_exists && variables[context].find(name) == variables[context].end()) {
+            THROW_UNDEFINED_VARIABLE_ERROR(name, value.token);
+        }
+        value.name                     = name;
+        value.context                  = context;
         this->variables[context][name] = value;
     }
 
@@ -69,6 +75,14 @@
         }
         THROW_UNDEFINED_VARIABLE_ERROR_HELPER(token.lexeme, token, file, line);
     };
+
+    [[nodiscard]] std::map<std::string, Value> getcontextVariables(const std::string & context) const {
+        auto it = variables.find(context);
+        if (it != variables.end()) {
+            return it->second;
+        }
+        throw std::runtime_error("Context not found: " + context);
+    }
 
     /**
          * Checks if a variable exists within the specified context.
@@ -110,12 +124,17 @@
 
     void handleFunctionCall(const std::vector<Token> & tokens, std::size_t & i);
     void handleVariableReference(const std::vector<Token> & tokens, std::size_t & i);
-    static void handleComment(std::size_t & i){ i++;}
-    static void handleSemicolon(std::size_t & i) {i++;};
+
+    static void handleComment(std::size_t & i) { i++; }
+
+    static void handleSemicolon(std::size_t & i) { 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);
+
+    std::string getContextName(const std::string & suffix) const { return this->filename + "::" + suffix; }
 };
 
 #endif  // SSCRIPTINTERPRETER_HPP
diff --git a/src/ScriptInterpreterHelpers.hpp b/src/ScriptInterpreterHelpers.hpp
index 9238a06..bff499f 100644
--- a/src/ScriptInterpreterHelpers.hpp
+++ b/src/ScriptInterpreterHelpers.hpp
@@ -39,7 +39,8 @@
     if (tokens[i].type != TokenType::StringDeclaration && tokens[i].type != TokenType::BooleanDeclaration &&
         tokens[i].type != TokenType::IntDeclaration && tokens[i].type != TokenType::DoubleDeclaration &&
         tokens[i].type != TokenType::RightParenthesis) {
-        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "variable declaration", file, line);
+        THROW_UNEXPECTED_TOKEN_ERROR_HELPER(tokens[i], "variable declaration: 'type $" + tokens[i].lexeme + "'", file,
+                                            line);
     }
     if (tokens[i].type != TokenType::RightParenthesis) {
         const auto parameter_type = getVariableTypeFromTokenTypeDeclaration(tokens[i].type);
@@ -47,9 +48,9 @@
             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_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);
@@ -74,9 +75,6 @@
 static void getFunctionBody(const std::vector<Token> & tokens, std::size_t & i, std::size_t & start,
                             std::size_t & end) {
     start = tokens[i].pos.end;
-    std::cout << "START Token: " << tokens[i].lexeme << " start pos: " << std::to_string(tokens[i].pos.start)
-              << " end pos: " << std::to_string(tokens[i].pos.end) << '\n';
-
     if (i >= tokens.size() || tokens[i].type != TokenType::LeftCurlyBracket) {
         THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "{");
     }
@@ -88,15 +86,12 @@
             continue;
         }
         if (tokens[i].type == TokenType::EndOfFile) {
-            throw std::runtime_error("Unexpected end of file");
+            THROW_UNEXPECTED_END_OF_FILE_ERROR(tokens[i]);
             break;
         }
         i++;
     }
     end = tokens[i].pos.start - 1;
-
-    std::cout << "END Token: " << tokens[i].lexeme << " start pos: " << std::to_string(tokens[i].pos.start)
-              << " end pos: " << std::to_string(tokens[i].pos.end) << '\n';
 
     if (i >= tokens.size() || tokens[i].type != TokenType::RightCurlyBracket) {
         THROW_UNEXPECTED_TOKEN_ERROR(tokens[i], "}");
diff --git a/src/Token.hpp b/src/Token.hpp
index 1391349..201d84b 100644
--- a/src/Token.hpp
+++ b/src/Token.hpp
@@ -1,6 +1,7 @@
 #ifndef TOKEN_HPP
 #define TOKEN_HPP
 #include <cstdint>
+#include <iostream>
 #include <string>
 #include <unordered_map>
 
@@ -26,7 +27,9 @@
     IntDeclaration,       // int $variable
     DoubleDeclaration,    // double $variable
     BooleanDeclaration,   // bool $variable
-    FunctionDeclaration,  // function $variable
+    FunctionDeclaration,  // function fn_name
+    FunctionCall,         // fn_name(args)
+    Return,               // return
     Equals,               // =
     Plus,                 // +
     Minus,                // -
@@ -73,6 +76,8 @@
     { TokenType::DoubleDeclaration,   "DoubleDeclaration"   },
     { TokenType::BooleanDeclaration,  "BooleanDeclaration"  },
     { TokenType::FunctionDeclaration, "FunctionDeclaration" },
+    { TokenType::FunctionCall,        "FunctionCall"        },
+    { TokenType::Return,              "Return"              },
     { TokenType::Equals,              "Equals"              },
     { TokenType::Plus,                "Plus"                },
     { TokenType::Minus,               "Minus"               },
@@ -155,9 +160,10 @@
     if (declaration == Variables::Type::VT_BOOLEAN) {
         return TokenType::BooleanDeclaration;
     }
-    if (declaration == Variables::Type::VT_FUNCTION) {
-        return TokenType::FunctionDeclaration;
-    }
+    // if (declaration == Variables::Type::VT_FUNCTION) {
+    //     return TokenType::FunctionDeclaration;
+    // }
+    std::cout << "Unknown variable type: " << Variables::TypeToString(declaration) << "\n";
     return TokenType::Unknown;
 }
 
@@ -174,9 +180,9 @@
     if (type == TokenType::BooleanDeclaration) {
         return Variables::Type::VT_BOOLEAN;
     }
-    if (type == TokenType::FunctionDeclaration) {
-        return Variables::Type::VT_FUNCTION;
-    }
+    //if (type == TokenType::FunctionDeclaration) {
+    //    return Variables::Type::VT_FUNCTION;
+    //}
     return Variables::Type::VT_NULL;
 };
 
diff --git a/src/Value.hpp b/src/Value.hpp
index 520e0de..8b649e6 100644
--- a/src/Value.hpp
+++ b/src/Value.hpp
@@ -12,6 +12,8 @@
     Variables::Type          type = Variables::Type::VT_NULL;
     Variables::DataContainer data;
     Token                    token;
+    std::string              name;
+    std::string              context;
 
     Value() : type(Variables::Type::VT_NULL) {}
 
diff --git a/src/VariableTypes.hpp b/src/VariableTypes.hpp
index e03c242..0f3f77d 100644
--- a/src/VariableTypes.hpp
+++ b/src/VariableTypes.hpp
@@ -10,7 +10,7 @@
 
 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_FUNCTION, VT_NOT_DEFINED };
+enum class Type : std::uint8_t { VT_INT, VT_DOUBLE, VT_STRING, VT_BOOLEAN, VT_NULL, VT_NOT_DEFINED };
 
 const std::unordered_map<std::string, Type> StringToTypeMap = {
     { "int",         Type::VT_INT         },
@@ -19,7 +19,6 @@
     { "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 = {
@@ -28,7 +27,6 @@
     { Type::VT_STRING,      "string"      },
     { Type::VT_BOOLEAN,     "bool"        },
     { Type::VT_NULL,        "null"        },
-    { Type::VT_FUNCTION,    "function"    },
     { Type::VT_NOT_DEFINED, "not_defined" },
 };
 

--
Gitblit v1.9.3