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